an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1namespace PDSharp.Core
2
3open System
4open System.Collections.Generic
5open System.Formats.Cbor
6open System.IO
7open System.Text
8
9module DagCbor =
10 type SortKey = { Length : int; Bytes : byte[] }
11
12 let private getSortKey (key : string) =
13 let bytes = Encoding.UTF8.GetBytes(key)
14 { Length = bytes.Length; Bytes = bytes }
15
16 let private compareKeys (a : string) (b : string) =
17 let ka = getSortKey a
18 let kb = getSortKey b
19
20 if ka.Length <> kb.Length then
21 ka.Length.CompareTo kb.Length
22 else
23 let mutable res = 0
24 let mutable i = 0
25
26 while res = 0 && i < ka.Bytes.Length do
27 res <- ka.Bytes.[i].CompareTo(kb.Bytes.[i])
28 i <- i + 1
29
30 res
31
32 let rec private writeItem (writer : CborWriter) (item : obj) =
33 match item with
34 | null -> writer.WriteNull()
35 | :? bool as b -> writer.WriteBoolean(b)
36 | :? int as i -> writer.WriteInt32(i)
37 | :? int64 as l -> writer.WriteInt64(l)
38 | :? string as s -> writer.WriteTextString(s)
39 | :? (byte[]) as b -> writer.WriteByteString(b)
40 | :? Cid as c ->
41 let tag = LanguagePrimitives.EnumOfValue<uint64, CborTag>(42UL)
42 writer.WriteTag(tag)
43 let rawCid = c.Bytes
44 let linkBytes = Array.zeroCreate<byte> (rawCid.Length + 1)
45 linkBytes.[0] <- 0x00uy
46 Array.Copy(rawCid, 0, linkBytes, 1, rawCid.Length)
47 writer.WriteByteString(linkBytes)
48
49 | :? Map<string, obj> as m ->
50 let keys = m |> Map.toList |> List.map fst |> List.sortWith compareKeys
51 writer.WriteStartMap(keys.Length)
52
53 for k in keys do
54 writer.WriteTextString(k)
55 writeItem writer (m.[k])
56
57 writer.WriteEndMap()
58
59 | :? IDictionary<string, obj> as d ->
60 let keys = d.Keys |> Seq.toList |> List.sortWith compareKeys
61 writer.WriteStartMap(d.Count)
62
63 for k in keys do
64 writer.WriteTextString(k)
65 writeItem writer (d.[k])
66
67 writer.WriteEndMap()
68
69 | :? seq<obj> as l ->
70 let arr = l |> Seq.toArray
71 writer.WriteStartArray(arr.Length)
72
73 for x in arr do
74 writeItem writer x
75
76 writer.WriteEndArray()
77
78 | _ -> failwith $"Unsupported type for DAG-CBOR: {item.GetType().Name}"
79
80 let encode (data : obj) : byte[] =
81 let writer = new CborWriter(CborConformanceMode.Strict, false, false)
82 writeItem writer data
83 writer.Encode()