an atproto pds written in F# (.NET 9) 馃
pds fsharp giraffe dotnet atproto
at main 3.0 kB view raw
1namespace PDSharp.Core 2 3open System 4open System.Text 5 6/// Minimal Base32 (RFC 4648 Lowercase) 7module Base32Encoding = 8 let private alphabet = "abcdefghijklmnopqrstuvwxyz234567" 9 10 let ToString (data : byte[]) : string = 11 if data.Length = 0 then 12 "" 13 else 14 let mutable i = 0 15 let mutable index = 0 16 let mutable digit = 0 17 let mutable currByte = 0 18 let mutable nextByte = 0 19 let sb = StringBuilder((data.Length + 7) * 8 / 5) 20 21 while i < data.Length do 22 currByte <- (int data.[i]) &&& 0xFF 23 24 if index > 3 then 25 if (i + 1) < data.Length then 26 nextByte <- (int data.[i + 1]) &&& 0xFF 27 else 28 nextByte <- 0 29 30 digit <- currByte &&& (0xFF >>> index) 31 index <- (index + 5) % 8 32 digit <- digit <<< index 33 digit <- digit ||| (nextByte >>> (8 - index)) 34 i <- i + 1 35 else 36 digit <- currByte >>> 8 - (index + 5) &&& 0x1F 37 index <- (index + 5) % 8 38 39 if index = 0 then 40 i <- i + 1 41 42 sb.Append(alphabet.[digit]) |> ignore 43 44 sb.ToString() 45 46 let FromString (s : string) : byte[] option = 47 if String.IsNullOrEmpty s then 48 Some [||] 49 else 50 try 51 let bits = s.Length * 5 52 let bytes = Array.zeroCreate<byte> (bits / 8) 53 let mutable buffer = 0 54 let mutable bitsInBuffer = 0 55 let mutable byteIndex = 0 56 57 for c in s do 58 let idx = alphabet.IndexOf(Char.ToLowerInvariant c) 59 60 if idx < 0 then 61 failwith "Invalid base32 character" 62 63 buffer <- buffer <<< 5 ||| idx 64 bitsInBuffer <- bitsInBuffer + 5 65 66 if bitsInBuffer >= 8 then 67 bitsInBuffer <- bitsInBuffer - 8 68 69 if byteIndex < bytes.Length then 70 bytes.[byteIndex] <- byte ((buffer >>> bitsInBuffer) &&& 0xFF) 71 byteIndex <- byteIndex + 1 72 73 Some bytes 74 with _ -> 75 None 76 77/// Basic CID implementation for AT Protocol (CIDv1 + dag-cbor + sha2-256) 78/// 79/// Constants for ATProto defaults: 80/// - Version 1 (0x01) 81/// - Codec: dag-cbor (0x71) 82/// - Hash: sha2-256 (0x12) - Length 32 (0x20) 83[<Struct>] 84type Cid = 85 val Bytes : byte[] 86 new(bytes : byte[]) = { Bytes = bytes } 87 88 static member FromHash(hash : byte[]) = 89 if hash.Length <> 32 then 90 failwith "Hash must be 32 bytes (sha2-256)" 91 92 let cidBytes = Array.zeroCreate<byte> 36 93 cidBytes.[0] <- 0x01uy 94 cidBytes.[1] <- 0x71uy 95 cidBytes.[2] <- 0x12uy 96 cidBytes.[3] <- 0x20uy 97 Array.Copy(hash, 0, cidBytes, 4, 32) 98 Cid cidBytes 99 100 static member TryParse(s : string) : Cid option = 101 if String.IsNullOrWhiteSpace s then 102 None 103 elif s.StartsWith("b") then 104 match Base32Encoding.FromString(s.Substring(1)) with 105 | Some bytes when bytes.Length = 36 -> Some(Cid bytes) 106 | _ -> None 107 else 108 None 109 110 override this.ToString() = 111 "b" + Base32Encoding.ToString(this.Bytes)