···11+namespace PDSharp.Core
22+33+open System
44+open System.Text
55+66+/// Minimal Base32 (RFC 4648 Lowercase)
77+module Base32Encoding =
88+ let private alphabet = "abcdefghijklmnopqrstuvwxyz234567"
99+1010+ let ToString (data : byte[]) : string =
1111+ if data.Length = 0 then
1212+ ""
1313+ else
1414+ let mutable i = 0
1515+ let mutable index = 0
1616+ let mutable digit = 0
1717+ let mutable currByte = 0
1818+ let mutable nextByte = 0
1919+ let sb = StringBuilder((data.Length + 7) * 8 / 5)
2020+2121+ while i < data.Length do
2222+ currByte <- (int data.[i]) &&& 0xFF
2323+2424+ if index > 3 then
2525+ if (i + 1) < data.Length then
2626+ nextByte <- (int data.[i + 1]) &&& 0xFF
2727+ else
2828+ nextByte <- 0
2929+3030+ digit <- currByte &&& (0xFF >>> index)
3131+ index <- (index + 5) % 8
3232+ digit <- digit <<< index
3333+ digit <- digit ||| (nextByte >>> (8 - index))
3434+ i <- i + 1
3535+ else
3636+ digit <- currByte >>> 8 - (index + 5) &&& 0x1F
3737+ index <- (index + 5) % 8
3838+3939+ if index = 0 then
4040+ i <- i + 1
4141+4242+ sb.Append(alphabet.[digit]) |> ignore
4343+4444+ sb.ToString()
4545+4646+/// Basic CID implementation for AT Protocol (CIDv1 + dag-cbor + sha2-256)
4747+///
4848+/// Constants for ATProto defaults:
4949+/// - Version 1 (0x01)
5050+/// - Codec: dag-cbor (0x71)
5151+/// - Hash: sha2-256 (0x12) - Length 32 (0x20)
5252+[<Struct>]
5353+type Cid =
5454+ val Bytes : byte[]
5555+ new(bytes : byte[]) = { Bytes = bytes }
5656+5757+ static member FromHash(hash : byte[]) =
5858+ if hash.Length <> 32 then
5959+ failwith "Hash must be 32 bytes (sha2-256)"
6060+6161+ let cidBytes = Array.zeroCreate<byte> 36
6262+ cidBytes.[0] <- 0x01uy
6363+ cidBytes.[1] <- 0x71uy
6464+ cidBytes.[2] <- 0x12uy
6565+ cidBytes.[3] <- 0x20uy
6666+ Array.Copy(hash, 0, cidBytes, 4, 32)
6767+ Cid cidBytes
6868+6969+ override this.ToString() =
7070+ "b" + Base32Encoding.ToString(this.Bytes)
+83
PDSharp.Core/DagCbor.fs
···11+namespace PDSharp.Core
22+33+open System
44+open System.Collections.Generic
55+open System.Formats.Cbor
66+open System.IO
77+open System.Text
88+99+module DagCbor =
1010+ type SortKey = { Length : int; Bytes : byte[] }
1111+1212+ let private getSortKey (key : string) =
1313+ let bytes = Encoding.UTF8.GetBytes(key)
1414+ { Length = bytes.Length; Bytes = bytes }
1515+1616+ let private compareKeys (a : string) (b : string) =
1717+ let ka = getSortKey a
1818+ let kb = getSortKey b
1919+2020+ if ka.Length <> kb.Length then
2121+ ka.Length.CompareTo kb.Length
2222+ else
2323+ let mutable res = 0
2424+ let mutable i = 0
2525+2626+ while res = 0 && i < ka.Bytes.Length do
2727+ res <- ka.Bytes.[i].CompareTo(kb.Bytes.[i])
2828+ i <- i + 1
2929+3030+ res
3131+3232+ let rec private writeItem (writer : CborWriter) (item : obj) =
3333+ match item with
3434+ | null -> writer.WriteNull()
3535+ | :? bool as b -> writer.WriteBoolean(b)
3636+ | :? int as i -> writer.WriteInt32(i)
3737+ | :? int64 as l -> writer.WriteInt64(l)
3838+ | :? string as s -> writer.WriteTextString(s)
3939+ | :? (byte[]) as b -> writer.WriteByteString(b)
4040+ | :? Cid as c ->
4141+ let tag = LanguagePrimitives.EnumOfValue<uint64, CborTag>(42UL)
4242+ writer.WriteTag(tag)
4343+ let rawCid = c.Bytes
4444+ let linkBytes = Array.zeroCreate<byte> (rawCid.Length + 1)
4545+ linkBytes.[0] <- 0x00uy
4646+ Array.Copy(rawCid, 0, linkBytes, 1, rawCid.Length)
4747+ writer.WriteByteString(linkBytes)
4848+4949+ | :? Map<string, obj> as m ->
5050+ let keys = m |> Map.toList |> List.map fst |> List.sortWith compareKeys
5151+ writer.WriteStartMap(keys.Length)
5252+5353+ for k in keys do
5454+ writer.WriteTextString(k)
5555+ writeItem writer (m.[k])
5656+5757+ writer.WriteEndMap()
5858+5959+ | :? IDictionary<string, obj> as d ->
6060+ let keys = d.Keys |> Seq.toList |> List.sortWith compareKeys
6161+ writer.WriteStartMap(d.Count)
6262+6363+ for k in keys do
6464+ writer.WriteTextString(k)
6565+ writeItem writer (d.[k])
6666+6767+ writer.WriteEndMap()
6868+6969+ | :? seq<obj> as l ->
7070+ let arr = l |> Seq.toArray
7171+ writer.WriteStartArray(arr.Length)
7272+7373+ for x in arr do
7474+ writeItem writer x
7575+7676+ writer.WriteEndArray()
7777+7878+ | _ -> failwith $"Unsupported type for DAG-CBOR: {item.GetType().Name}"
7979+8080+ let encode (data : obj) : byte[] =
8181+ let writer = new CborWriter(CborConformanceMode.Strict, false, false)
8282+ writeItem writer data
8383+ writer.Encode()