Skip to content

Commit 0cfdf7a

Browse files
committed
add docstrings
1 parent daf1bb6 commit 0cfdf7a

9 files changed

Lines changed: 138 additions & 9 deletions

File tree

.idea/artifacts/benchmark_js.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/artifacts/benchmark_wasm_js.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
**Note on versioning:** This project follows semantic versioning with an emphasis on ABI compatibility. Major version numbers will be bumped when changes break ABI compatibility, even if the API remains backward compatible.
9+
810
## [Unreleased]
911

12+
### Added
13+
- Added support for binary format.
14+
- Added an option to disable secure random for ULIDs.
15+
- Added KDocs for all public APIs.
16+
1017
### Changed
1118
- Kulid is now Kotlin [explicit api mode](https://kotlinlang.org/docs/api-guidelines-simplicity.html#use-explicit-api-mode) compliant.
1219
- Bump AGP version to 8.10.1
13-
- Add benchmark harness, comparing to Kotlin's UUID implementation
20+
- Add benchmark harness, comparing Kulid to Kotlin's UUID implementation
1421

1522
## [0.2.0] - 2025-06-17
1623

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ ULIDs are identifiers that provide:
2525
- Strongly typed with low overhead by leveraging kotlin [value classes](https://kotlinlang.org/docs/inline-classes.html)
2626
- Benchmark suite for performance testing
2727

28+
## Versioning
29+
30+
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) with an emphasis on ABI compatibility. Major version numbers will be bumped when changes break ABI compatibility, even if the API remains backward compatible.
31+
32+
For detailed information about changes between versions, please see the [CHANGELOG.md](CHANGELOG.md) file.
33+
2834
## TODOs
2935
This library is still being developed! As a result, there are still features of the [ULID spec](https://github.com/ulid/spec) that are missing, namely:
3036
- [ ] [Monotonicity](https://github.com/ulid/spec?tab=readme-ov-file#monotonicity)
31-
- [ ] [Binary Layout](https://github.com/ulid/spec?tab=readme-ov-file#binary-layout-and-byte-order)
32-
- [ ] Parsing ULIDs from string
37+
- [x] [Binary Layout](https://github.com/ulid/spec?tab=readme-ov-file#binary-layout-and-byte-order)
3338

3439
## Installation
3540

kulid/api/android/kulid.api

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public final class dev/phillipslabs/kulid/ULID : java/lang/Comparable {
1717

1818
public final class dev/phillipslabs/kulid/ULID$Companion {
1919
public final fun fromString-cf8O3LM (Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString;
20-
public final fun generate-cf8O3LM (J)Lkotlinx/io/bytestring/ByteString;
21-
public static synthetic fun generate-cf8O3LM$default (Ldev/phillipslabs/kulid/ULID$Companion;JILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
20+
public final fun generate-VvTLzkI (JZ)Lkotlinx/io/bytestring/ByteString;
21+
public static synthetic fun generate-VvTLzkI$default (Ldev/phillipslabs/kulid/ULID$Companion;JZILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
2222
public final fun getMAX-0mmbifs ()Lkotlinx/io/bytestring/ByteString;
2323
public final fun getMIN-0mmbifs ()Lkotlinx/io/bytestring/ByteString;
2424
public final fun serializer ()Lkotlinx/serialization/KSerializer;

kulid/api/jvm/kulid.api

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public final class dev/phillipslabs/kulid/ULID : java/lang/Comparable {
1717

1818
public final class dev/phillipslabs/kulid/ULID$Companion {
1919
public final fun fromString-cf8O3LM (Ljava/lang/String;)Lkotlinx/io/bytestring/ByteString;
20-
public final fun generate-cf8O3LM (J)Lkotlinx/io/bytestring/ByteString;
21-
public static synthetic fun generate-cf8O3LM$default (Ldev/phillipslabs/kulid/ULID$Companion;JILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
20+
public final fun generate-VvTLzkI (JZ)Lkotlinx/io/bytestring/ByteString;
21+
public static synthetic fun generate-VvTLzkI$default (Ldev/phillipslabs/kulid/ULID$Companion;JZILjava/lang/Object;)Lkotlinx/io/bytestring/ByteString;
2222
public final fun getMAX-0mmbifs ()Lkotlinx/io/bytestring/ByteString;
2323
public final fun getMIN-0mmbifs ()Lkotlinx/io/bytestring/ByteString;
2424
public final fun serializer ()Lkotlinx/serialization/KSerializer;

kulid/api/kulid.klib.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ final value class dev.phillipslabs.kulid/ULID : kotlin/Comparable<dev.phillipsla
2222
final fun <get-MIN>(): dev.phillipslabs.kulid/ULID // dev.phillipslabs.kulid/ULID.Companion.MIN.<get-MIN>|<get-MIN>(){}[0]
2323

2424
final fun fromString(kotlin/String): dev.phillipslabs.kulid/ULID // dev.phillipslabs.kulid/ULID.Companion.fromString|fromString(kotlin.String){}[0]
25-
final fun generate(kotlin/Long = ...): dev.phillipslabs.kulid/ULID // dev.phillipslabs.kulid/ULID.Companion.generate|generate(kotlin.Long){}[0]
25+
final fun generate(kotlin/Long = ..., kotlin/Boolean = ...): dev.phillipslabs.kulid/ULID // dev.phillipslabs.kulid/ULID.Companion.generate|generate(kotlin.Long;kotlin.Boolean){}[0]
2626
final fun serializer(): kotlinx.serialization/KSerializer<dev.phillipslabs.kulid/ULID> // dev.phillipslabs.kulid/ULID.Companion.serializer|serializer(){}[0]
2727
}
2828
}

kulid/src/commonMain/kotlin/dev/phillipslabs/kulid/ULID.kt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ private const val ULID_ENCODING_FRONT_PADDING = ENCODED_ULID_BYTE_SIZE * 5 - ULI
3030
// Crockford's base32 alphabet
3131
private val ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray()
3232

33+
/**
34+
* A Universally Unique Lexicographically Sortable Identifier (ULID).
35+
*
36+
* ULIDs are 128-bit identifiers that combine a timestamp with random data to create
37+
* unique, lexicographically sortable identifiers. They are encoded as 26-character
38+
* Crockford Base32 strings.
39+
*
40+
* Features:
41+
* - 128-bit compatibility with UUID
42+
* - Lexicographically sortable (by time, then random component)
43+
* - Canonically encoded as a 26 character string
44+
* - Uses Crockford's base32 for better human readability
45+
* - Case insensitive
46+
* - No special characters (URL safe)
47+
*
48+
* @property value The raw binary representation of the ULID as a ByteString.
49+
*/
3350
@Serializable(with = ULIDSerializer::class)
3451
@JvmInline
3552
public value class ULID private constructor(
@@ -39,14 +56,61 @@ public value class ULID private constructor(
3956
require(value.size == ULID_BYTE_SIZE) { "ULID should have $ULID_BYTE_SIZE bytes, but had ${value.size}" }
4057
}
4158

59+
/**
60+
* Compares this ULID with another ULID lexicographically.
61+
*
62+
* ULIDs are designed to be lexicographically sortable, with earlier timestamps
63+
* sorting before later ones. ULIDs created within the same millisecond are
64+
* sorted by their random component.
65+
*
66+
* @param other The ULID to compare with.
67+
* @return A negative value if this ULID is less than [other], zero if they're equal,
68+
* or a positive value if this ULID is greater than [other].
69+
*/
4270
override fun compareTo(other: ULID): Int = value.compareTo(other.value)
4371

72+
/**
73+
* Returns the string representation of this ULID in Crockford Base32 encoding.
74+
*
75+
* @return A 26-character Crockford Base32 encoded string.
76+
*/
4477
override fun toString(): String = encodeCrockfordBase32(value)
4578

79+
/**
80+
* Companion object providing factory methods and constants for ULID creation and manipulation.
81+
*/
4682
public companion object {
83+
/**
84+
* The maximum possible ULID value.
85+
*
86+
* This constant represents the largest possible ULID value, with all bits set to 1,
87+
* encoded as "7ZZZZZZZZZZZZZZZZZZZZZZZZZ" in Crockford Base32.
88+
*/
4789
public val MAX: ULID = fromString("7ZZZZZZZZZZZZZZZZZZZZZZZZZ")
90+
91+
/**
92+
* The minimum possible ULID value.
93+
*
94+
* This constant represents the smallest possible ULID value, with all bits set to 0,
95+
* encoded as "00000000000000000000000000" in Crockford Base32.
96+
*/
4897
public val MIN: ULID = fromString("00000000000000000000000000")
4998

99+
/**
100+
* Generates a new ULID.
101+
*
102+
* By default, this method uses the current system time as the timestamp component
103+
* and cryptographically secure random data for the random component.
104+
*
105+
* @param timestamp The timestamp to use in milliseconds since the Unix epoch.
106+
* Defaults to the current system time.
107+
* @param secureRandom Whether to use cryptographically secure random data.
108+
* Defaults to true. Set to false only if you don't need
109+
* cryptographic security and want better performance.
110+
* @return A new ULID instance.
111+
* @throws IllegalStateException If the timestamp is negative or exceeds the maximum
112+
* supported timestamp value (2^48 - 1).
113+
*/
50114
public fun generate(
51115
timestamp: Long = Clock.System.now().toEpochMilliseconds(),
52116
secureRandom: Boolean = true,
@@ -73,6 +137,16 @@ public value class ULID private constructor(
73137
return ULID(buf.readByteString())
74138
}
75139

140+
/**
141+
* Creates a ULID from its string representation.
142+
*
143+
* This method parses a Crockford Base32 encoded ULID string and creates a new ULID instance.
144+
*
145+
* @param encoded The Crockford Base32 encoded ULID string (26 characters).
146+
* @return A new ULID instance.
147+
* @throws IllegalArgumentException If the encoded string is not exactly 26 characters long
148+
* or contains invalid characters.
149+
*/
76150
public fun fromString(encoded: String): ULID {
77151
require(encoded.length == ENCODED_ULID_BYTE_SIZE) { "Encoded ULID must be $ENCODED_ULID_BYTE_SIZE characters long" }
78152
val buf = Buffer()
@@ -111,6 +185,16 @@ public value class ULID private constructor(
111185
return ULID(buf.readByteString())
112186
}
113187

188+
/**
189+
* Encodes a ByteString as a Crockford Base32 string.
190+
*
191+
* This internal function converts the raw binary representation of a ULID
192+
* to its canonical 26-character Crockford Base32 string representation.
193+
*
194+
* @param bytes The ByteString to encode (must be exactly 16 bytes).
195+
* @return A 26-character Crockford Base32 encoded string.
196+
* @throws IllegalArgumentException If the ByteString is not exactly 16 bytes.
197+
*/
114198
internal fun encodeCrockfordBase32(bytes: ByteString) =
115199
buildString(ENCODED_ULID_BYTE_SIZE) {
116200
require(bytes.size == ULID_BYTE_SIZE) { "ULID should have $ULID_BYTE_SIZE bytes, but had ${bytes.size}" }

kulid/src/commonMain/kotlin/dev/phillipslabs/kulid/ULIDSerializer.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,37 @@ import kotlinx.serialization.descriptors.SerialDescriptor
88
import kotlinx.serialization.encoding.Decoder
99
import kotlinx.serialization.encoding.Encoder
1010

11-
@OptIn(ExperimentalSerializationApi::class)
11+
/**
12+
* Serializer for the ULID class using kotlinx.serialization.
13+
*
14+
* This serializer converts ULIDs to and from their string representation for serialization purposes.
15+
* ULIDs are serialized as 26-character Crockford Base32 encoded strings.
16+
*/
1217
public object ULIDSerializer : KSerializer<ULID> {
18+
/**
19+
* The serial descriptor for ULID, describing it as a primitive string.
20+
*/
1321
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ULID", PrimitiveKind.STRING)
1422

23+
/**
24+
* Serializes a ULID to its string representation.
25+
*
26+
* @param encoder The encoder to write the serialized form to.
27+
* @param value The ULID to serialize.
28+
*/
1529
override fun serialize(
1630
encoder: Encoder,
1731
value: ULID,
1832
) {
1933
encoder.encodeString(value.toString())
2034
}
2135

36+
/**
37+
* Deserializes a ULID from its string representation.
38+
*
39+
* @param decoder The decoder to read the serialized form from.
40+
* @return The deserialized ULID.
41+
* @throws IllegalArgumentException If the string is not a valid ULID representation.
42+
*/
2243
override fun deserialize(decoder: Decoder): ULID = ULID.fromString(decoder.decodeString())
2344
}

0 commit comments

Comments
 (0)