an atproto pds written in F# (.NET 9) 馃
pds fsharp giraffe dotnet atproto
at main 24 kB view raw
1namespace PDSharp.Core 2 3open System 4open System.Collections.Generic 5open System.Formats.Cbor 6 7module Mst = 8 type MstEntry = { 9 PrefixLen : int 10 KeySuffix : string 11 Value : Cid 12 Tree : Cid option 13 } 14 15 type MstNode = { Left : Cid option; Entries : MstEntry list } 16 17 /// Layer Calculation 18 let layer (key : string) : int = 19 let hash = Crypto.sha256Str key 20 let mutable zeros = 0 21 let mutable i = 0 22 let mutable found = false 23 24 while i < hash.Length && not found do 25 let b = hash.[i] 26 27 if b = 0uy then 28 zeros <- zeros + 8 29 i <- i + 1 30 else 31 let mutable mask = 0x80uy 32 33 while b &&& mask = 0uy do 34 zeros <- zeros + 1 35 mask <- mask >>> 1 36 37 found <- true 38 39 zeros / 2 40 41 let entryToCborObj (e : MstEntry) : obj = 42 let arr = Array.zeroCreate<obj> 4 43 arr.[0] <- e.PrefixLen :> obj 44 arr.[1] <- e.KeySuffix :> obj 45 arr.[2] <- e.Value :> obj 46 47 arr.[3] <- 48 match e.Tree with 49 | Some c -> c :> obj 50 | None -> null 51 52 arr :> obj 53 54 let nodeToCborObj (node : MstNode) : obj = 55 let arr = Array.zeroCreate<obj> 2 56 57 arr.[0] <- 58 match node.Left with 59 | Some c -> c :> obj 60 | None -> null 61 62 let entriesArr = node.Entries |> List.map entryToCborObj |> Seq.toArray 63 arr.[1] <- entriesArr :> obj 64 arr :> obj 65 66 let serialize (node : MstNode) : byte[] = nodeToCborObj node |> DagCbor.encode 67 68 let readCid (reader : CborReader) = 69 let tag = reader.ReadTag() 70 let tag42 = LanguagePrimitives.EnumOfValue<uint64, CborTag>(42UL) 71 72 if tag <> tag42 then 73 failwith "Expected CID tag 42" 74 75 let bytes = reader.ReadByteString() 76 77 if bytes.Length > 0 && bytes.[0] = 0x00uy then 78 let raw = Array.zeroCreate<byte> (bytes.Length - 1) 79 Array.Copy(bytes, 1, raw, 0, raw.Length) 80 Cid raw 81 else 82 Cid bytes 83 84 let deserialize (data : byte[]) : MstNode = 85 let reader = new CborReader(data.AsMemory(), CborConformanceMode.Strict) 86 let len = reader.ReadStartArray() 87 88 if len.HasValue && len.Value <> 2 then 89 failwith "MST node must be array of length 2" 90 91 let left = 92 match reader.PeekState() with 93 | CborReaderState.Null -> 94 reader.ReadNull() 95 None 96 | _ -> Some(readCid reader) 97 98 let entriesLen = reader.ReadStartArray() 99 let mutable entries = [] 100 101 let count = 102 if entriesLen.HasValue then 103 entriesLen.Value 104 else 105 Int32.MaxValue 106 107 let mutable i = 0 108 109 while i < count && reader.PeekState() <> CborReaderState.EndArray do 110 let entryLen = reader.ReadStartArray() 111 112 if entryLen.HasValue && entryLen.Value <> 4 then 113 failwith "MST entry must be array of length 4" 114 115 let p = reader.ReadInt32() 116 let k = reader.ReadTextString() 117 let v = readCid reader 118 119 let t = 120 match reader.PeekState() with 121 | CborReaderState.Null -> 122 reader.ReadNull() 123 None 124 | _ -> Some(readCid reader) 125 126 reader.ReadEndArray() 127 128 entries <- entries @ [ { PrefixLen = p; KeySuffix = k; Value = v; Tree = t } ] 129 i <- i + 1 130 131 reader.ReadEndArray() 132 reader.ReadEndArray() 133 { Left = left; Entries = entries } 134 135 let compareKeys (a : string) (b : string) : int = 136 let bytesA = System.Text.Encoding.UTF8.GetBytes(a) 137 let bytesB = System.Text.Encoding.UTF8.GetBytes(b) 138 let len = min bytesA.Length bytesB.Length 139 let mutable res = 0 140 let mutable i = 0 141 142 while res = 0 && i < len do 143 res <- bytesA.[i].CompareTo(bytesB.[i]) 144 i <- i + 1 145 146 if res <> 0 then 147 res 148 else 149 bytesA.Length.CompareTo bytesB.Length 150 151 type NodeLoader = Cid -> Async<MstNode option> 152 type NodePersister = MstNode -> Async<Cid> 153 154 let storeNode (persister : NodePersister) (node : MstNode) : Async<Cid> = persister node 155 156 let rec get (loader : NodeLoader) (node : MstNode) (key : string) (prevKey : string) : Async<Cid option> = async { 157 let mutable currentKey = prevKey 158 let mutable foundEntry : MstEntry option = None 159 let mutable nextTree : Cid option = node.Left 160 let mutable stop = false 161 let mutable i = 0 162 163 while not stop && i < node.Entries.Length do 164 let e = node.Entries.[i] 165 166 let prefix = 167 if e.PrefixLen > currentKey.Length then 168 currentKey 169 else 170 currentKey.Substring(0, e.PrefixLen) 171 172 let fullKey = prefix + e.KeySuffix 173 let cmp = compareKeys key fullKey 174 175 if cmp = 0 then 176 foundEntry <- Some e 177 stop <- true 178 elif cmp < 0 then 179 stop <- true 180 else 181 nextTree <- e.Tree 182 currentKey <- fullKey 183 i <- i + 1 184 185 match foundEntry with 186 | Some e -> return Some e.Value 187 | None -> 188 match nextTree with 189 | None -> return None 190 | Some cid -> 191 let! child = loader cid 192 193 match child with 194 | Some cNode -> return! get loader cNode key currentKey 195 | None -> return None 196 } 197 198 let sharedPrefixLen (a : string) (b : string) = 199 let len = min a.Length b.Length 200 let mutable i = 0 201 202 while i < len && a.[i] = b.[i] do 203 i <- i + 1 204 205 i 206 207 let nodeLayer (node : MstNode) (prevKey : string) = 208 match node.Entries with 209 | [] -> -1 210 | first :: _ -> 211 let prefix = 212 if first.PrefixLen > prevKey.Length then 213 prevKey 214 else 215 prevKey.Substring(0, first.PrefixLen) 216 217 let fullKey = prefix + first.KeySuffix 218 layer fullKey 219 220 /// Splits a node around a key (which is assumed to be higher layer than any in the node). 221 /// 222 /// Returns (LeftNode, RightNode). 223 let rec split 224 (loader : NodeLoader) 225 (persister : NodePersister) 226 (node : MstNode) 227 (key : string) 228 (prevKey : string) 229 : Async<MstNode * MstNode> = 230 async { 231 let mutable splitIdx = -1 232 let mutable found = false 233 let mutable currentKey = prevKey 234 let mutable i = 0 235 let entries = node.Entries 236 let mutable splitPrevKey = prevKey 237 238 while i < entries.Length && not found do 239 let e = entries.[i] 240 241 let prefix = 242 if e.PrefixLen > currentKey.Length then 243 currentKey 244 else 245 currentKey.Substring(0, e.PrefixLen) 246 247 let fullKey = prefix + e.KeySuffix 248 249 if compareKeys key fullKey < 0 then 250 splitIdx <- i 251 found <- true 252 else 253 currentKey <- fullKey 254 splitPrevKey <- currentKey 255 i <- i + 1 256 257 let childCidToSplit = 258 if found then 259 if splitIdx = 0 then 260 node.Left 261 else 262 entries.[splitIdx - 1].Tree 263 else if entries.Length = 0 then 264 node.Left 265 else 266 entries.[entries.Length - 1].Tree 267 268 let! (lChild, rChild) = async { 269 match childCidToSplit with 270 | None -> return ({ Left = None; Entries = [] }, { Left = None; Entries = [] }) 271 | Some cid -> 272 let! childNodeOpt = loader cid 273 274 match childNodeOpt with 275 | None -> return ({ Left = None; Entries = [] }, { Left = None; Entries = [] }) 276 | Some childNode -> return! split loader persister childNode key currentKey 277 } 278 279 let persistOrNone (n : MstNode) = async { 280 if n.Entries.IsEmpty && n.Left.IsNone then 281 return None 282 else 283 let! c = persister n 284 return Some c 285 } 286 287 let! lCid = persistOrNone lChild 288 let! rCid = persistOrNone rChild 289 let leftEntries = if found then entries |> List.take splitIdx else entries 290 291 let newLeftEntries = 292 if leftEntries.IsEmpty then 293 [] 294 else 295 let last = leftEntries.[leftEntries.Length - 1] 296 let newLast = { last with Tree = lCid } 297 (leftEntries |> List.take (leftEntries.Length - 1)) @ [ newLast ] 298 299 let leftNode = 300 if leftEntries.IsEmpty then 301 { Left = lCid; Entries = [] } 302 else 303 { Left = node.Left; Entries = newLeftEntries } 304 305 let rightEntries = if found then entries |> List.skip splitIdx else [] 306 307 let newRightEntries = 308 match rightEntries with 309 | [] -> [] 310 | first :: rest -> 311 let firstFullKey = 312 let prefix = 313 if first.PrefixLen > currentKey.Length then 314 currentKey 315 else 316 currentKey.Substring(0, first.PrefixLen) 317 318 prefix + first.KeySuffix 319 320 let newP = sharedPrefixLen key firstFullKey 321 let newSuffix = firstFullKey.Substring(newP) 322 let newFirst = { first with PrefixLen = newP; KeySuffix = newSuffix } 323 newFirst :: rest 324 325 let rightNode = { Left = rCid; Entries = newRightEntries } 326 return (leftNode, rightNode) 327 } 328 329 let rec put 330 (loader : NodeLoader) 331 (persister : NodePersister) 332 (node : MstNode) 333 (key : string) 334 (value : Cid) 335 (prevKey : string) 336 : Async<MstNode> = 337 async { 338 let kLayer = layer key 339 340 let nLayer = 341 match node.Entries with 342 | [] -> -1 343 | first :: _ -> 344 let prefix = 345 if first.PrefixLen > prevKey.Length then 346 prevKey 347 else 348 prevKey.Substring(0, first.PrefixLen) 349 350 let fullKey = prefix + first.KeySuffix 351 layer fullKey 352 353 if kLayer > nLayer then 354 let! (lNode, rNode) = split loader persister node key prevKey 355 356 let persistOrNone (n : MstNode) = async { 357 if n.Entries.IsEmpty && n.Left.IsNone then 358 return None 359 else 360 let! c = persister n 361 return Some c 362 } 363 364 let! lCid = persistOrNone lNode 365 let! rCid = persistOrNone rNode 366 367 let p = sharedPrefixLen prevKey key 368 let suffix = key.Substring(p) 369 370 return { 371 Left = lCid 372 Entries = [ 373 { 374 PrefixLen = p 375 KeySuffix = suffix 376 Value = value 377 Tree = rCid 378 } 379 ] 380 } 381 382 elif kLayer < nLayer then 383 let mutable nextCid = node.Left 384 let mutable currentKey = prevKey 385 let mutable found = false 386 let mutable i = 0 387 388 let entries = node.Entries 389 let mutable childIdx = -1 390 391 while i < entries.Length && not found do 392 let e = entries.[i] 393 394 let prefix = 395 if e.PrefixLen > currentKey.Length then 396 currentKey 397 else 398 currentKey.Substring(0, e.PrefixLen) 399 400 let fullKey = prefix + e.KeySuffix 401 402 if compareKeys key fullKey < 0 then 403 found <- true 404 else 405 childIdx <- i 406 nextCid <- e.Tree 407 currentKey <- fullKey 408 i <- i + 1 409 410 let! childNode = 411 match nextCid with 412 | Some c -> loader c 413 | None -> async { return Some { Left = None; Entries = [] } } 414 415 match childNode with 416 | None -> return failwith "Failed to load child node" 417 | Some cn -> 418 let! newChildNode = put loader persister cn key value currentKey 419 let! newChildCid = persister newChildNode 420 421 if childIdx = -1 then 422 return { node with Left = Some newChildCid } 423 else 424 return { 425 node with 426 Entries = 427 entries 428 |> List.mapi (fun idx x -> 429 if idx = childIdx then 430 { entries.[childIdx] with Tree = Some newChildCid } 431 else 432 x) 433 } 434 435 else 436 let mutable insertIdx = -1 437 let mutable found = false 438 let mutable currentKey = prevKey 439 let mutable i = 0 440 let mutable targetChildCid = None 441 let mutable childPrevKey = prevKey 442 443 let fullKeysCache = new List<string>() 444 445 while i < node.Entries.Length && not found do 446 let e = node.Entries.[i] 447 448 let prefix = 449 if e.PrefixLen > currentKey.Length then 450 currentKey 451 else 452 currentKey.Substring(0, e.PrefixLen) 453 454 let fullKey = prefix + e.KeySuffix 455 fullKeysCache.Add(fullKey) 456 457 let cmp = compareKeys key fullKey 458 459 if cmp = 0 then 460 insertIdx <- i 461 found <- true 462 elif cmp < 0 then 463 insertIdx <- i 464 targetChildCid <- if i = 0 then node.Left else node.Entries.[i - 1].Tree 465 childPrevKey <- currentKey 466 found <- true 467 else 468 currentKey <- fullKey 469 i <- i + 1 470 471 if not found then 472 insertIdx <- node.Entries.Length 473 474 targetChildCid <- 475 if node.Entries.Length = 0 then 476 node.Left 477 else 478 node.Entries.[node.Entries.Length - 1].Tree 479 480 childPrevKey <- currentKey 481 482 let entries = node.Entries 483 484 if found && compareKeys key fullKeysCache.[insertIdx] = 0 then 485 let e = entries.[insertIdx] 486 let newE = { e with Value = value } 487 488 let newEntries = 489 entries |> List.mapi (fun idx x -> if idx = insertIdx then newE else x) 490 491 return { node with Entries = newEntries } 492 else 493 let! (lChild, rChild) = async { 494 match targetChildCid with 495 | None -> return { Left = None; Entries = [] }, { Left = None; Entries = [] } 496 | Some cid -> 497 let! cOpt = loader cid 498 499 match cOpt with 500 | None -> return { Left = None; Entries = [] }, { Left = None; Entries = [] } 501 | Some c -> return! split loader persister c key childPrevKey 502 } 503 504 let persistOrNone (n : MstNode) = async { 505 if n.Entries.IsEmpty && n.Left.IsNone then 506 return None 507 else 508 let! c = persister n 509 return Some c 510 } 511 512 let! lCid = persistOrNone lChild 513 let! rCid = persistOrNone rChild 514 515 let beforeEntries = 516 if insertIdx = 0 then 517 [] 518 else 519 let prevE = entries.[insertIdx - 1] 520 let newPrevE = { prevE with Tree = lCid } 521 (entries |> List.take (insertIdx - 1)) @ [ newPrevE ] 522 523 let newLeft = if insertIdx = 0 then lCid else node.Left 524 let getFullKey idx = fullKeysCache.[idx] 525 let p = sharedPrefixLen childPrevKey key 526 let suffix = key.Substring(p) 527 528 let newEntry = { 529 PrefixLen = p 530 KeySuffix = suffix 531 Value = value 532 Tree = rCid 533 } 534 535 let afterEntries = 536 if insertIdx >= entries.Length then 537 [] 538 else 539 let first = entries.[insertIdx] 540 let firstFullKey = getFullKey insertIdx 541 542 let newP = sharedPrefixLen key firstFullKey 543 let newS = firstFullKey.Substring(newP) 544 let newFirst = { first with PrefixLen = newP; KeySuffix = newS } 545 546 [ newFirst ] @ (entries |> List.skip (insertIdx + 1)) 547 548 let newEntries = beforeEntries @ [ newEntry ] @ afterEntries 549 550 return { Left = newLeft; Entries = newEntries } 551 } 552 553 // --- Merge Operation --- 554 let rec merge 555 (loader : NodeLoader) 556 (persister : NodePersister) 557 (leftCid : Cid option) 558 (rightCid : Cid option) 559 (prevKey : string) 560 : Async<MstNode> = 561 async { 562 match leftCid, rightCid with 563 | None, None -> return { Left = None; Entries = [] } 564 | Some l, None -> 565 let! n = loader l 566 return n |> Option.defaultValue { Left = None; Entries = [] } 567 | None, Some r -> 568 let! n = loader r 569 return n |> Option.defaultValue { Left = None; Entries = [] } 570 | Some l, Some r -> 571 let! lNodeOpt = loader l 572 let! rNodeOpt = loader r 573 let lNode = lNodeOpt |> Option.defaultValue { Left = None; Entries = [] } 574 let rNode = rNodeOpt |> Option.defaultValue { Left = None; Entries = [] } 575 576 let lLayer = nodeLayer lNode prevKey 577 let mutable current = prevKey 578 579 for e in lNode.Entries do 580 let p = 581 if e.PrefixLen > current.Length then 582 current 583 else 584 current.Substring(0, e.PrefixLen) 585 586 current <- p + e.KeySuffix 587 588 let rightPrevKey = current 589 590 let realLLayer = nodeLayer lNode prevKey 591 let realRLayer = nodeLayer rNode rightPrevKey 592 593 if realLLayer > realRLayer then 594 match lNode.Entries with 595 | [] -> return! merge loader persister lNode.Left rightCid prevKey 596 | entries -> 597 let lastIdx = entries.Length - 1 598 let lastEntry = entries.[lastIdx] 599 600 let! mergedChild = merge loader persister lastEntry.Tree rightCid rightPrevKey 601 let! mergedCid = persister mergedChild 602 603 let newEntry = { lastEntry with Tree = Some mergedCid } 604 let newEntries = (entries |> List.take lastIdx) @ [ newEntry ] 605 return { lNode with Entries = newEntries } 606 607 elif realRLayer > realLLayer then 608 match rNode.Entries with 609 | [] -> return! merge loader persister leftCid rNode.Left prevKey 610 | _ -> 611 let! mergedChild = merge loader persister leftCid rNode.Left prevKey 612 let! mergedCid = persister mergedChild 613 614 return { rNode with Left = Some mergedCid } 615 616 else 617 let boundaryL = 618 match lNode.Entries with 619 | [] -> lNode.Left 620 | es -> es.[es.Length - 1].Tree 621 622 let boundaryR = rNode.Left 623 624 let! mergedBoundaryNode = merge loader persister boundaryL boundaryR rightPrevKey 625 let! mergedBoundaryCid = persister mergedBoundaryNode 626 627 let newEntries = 628 match lNode.Entries with 629 | [] -> rNode.Entries 630 | lEntries -> 631 let lastIdx = lEntries.Length - 1 632 let lastE = lEntries.[lastIdx] 633 let newLastE = { lastE with Tree = Some mergedBoundaryCid } 634 (lEntries |> List.take lastIdx) @ [ newLastE ] @ rNode.Entries 635 636 let newLeft = 637 if lNode.Entries.IsEmpty then 638 Some mergedBoundaryCid 639 else 640 lNode.Left 641 642 return { Left = newLeft; Entries = newEntries } 643 } 644 645 let rec delete 646 (loader : NodeLoader) 647 (persister : NodePersister) 648 (node : MstNode) 649 (key : string) 650 (prevKey : string) 651 : Async<MstNode option> = 652 async { 653 let mutable currentKey = prevKey 654 let mutable foundIdx = -1 655 let mutable nextTreeIdx = -1 656 let mutable i = 0 657 let mutable found = false 658 659 while i < node.Entries.Length && not found do 660 let e = node.Entries.[i] 661 662 let prefix = 663 if e.PrefixLen > currentKey.Length then 664 currentKey 665 else 666 currentKey.Substring(0, e.PrefixLen) 667 668 let fullKey = prefix + e.KeySuffix 669 670 let cmp = compareKeys key fullKey 671 672 if cmp = 0 then 673 foundIdx <- i 674 found <- true 675 elif cmp < 0 then 676 found <- true 677 nextTreeIdx <- i - 1 678 else 679 currentKey <- fullKey 680 i <- i + 1 681 682 if not found then 683 nextTreeIdx <- node.Entries.Length - 1 684 685 if foundIdx <> -1 then 686 let e = node.Entries.[foundIdx] 687 688 let leftChildCid = 689 if foundIdx = 0 then 690 node.Left 691 else 692 node.Entries.[foundIdx - 1].Tree 693 694 let rightChildCid = e.Tree 695 696 let mergePrevKey = if foundIdx = 0 then prevKey else currentKey 697 698 let! mergedChildNode = merge loader persister leftChildCid rightChildCid mergePrevKey 699 let! mergedChildCid = persister mergedChildNode 700 701 let newEntries = 702 if foundIdx = 0 then 703 let rest = node.Entries |> List.skip 1 704 705 match rest with 706 | [] -> [] 707 | first :: rs -> 708 let firstFullKey = 709 let oldP = 710 if first.PrefixLen > key.Length then 711 key 712 else 713 key.Substring(0, first.PrefixLen) 714 715 oldP + first.KeySuffix 716 717 let newP = sharedPrefixLen prevKey firstFullKey 718 let newSuffix = firstFullKey.Substring(newP) 719 let newFirst = { first with PrefixLen = newP; KeySuffix = newSuffix } 720 newFirst :: rs 721 else 722 let before = node.Entries |> List.take (foundIdx - 1) 723 let prevE = node.Entries.[foundIdx - 1] 724 let newPrevE = { prevE with Tree = Some mergedChildCid } 725 726 let rest = node.Entries |> List.skip (foundIdx + 1) 727 728 let newRest = 729 match rest with 730 | [] -> [] 731 | first :: rs -> 732 let firstFullKey = 733 let oldP = 734 if first.PrefixLen > key.Length then 735 key 736 else 737 key.Substring(0, first.PrefixLen) 738 739 oldP + first.KeySuffix 740 741 let newP = sharedPrefixLen mergePrevKey firstFullKey 742 let newSuffix = firstFullKey.Substring(newP) 743 let newFirst = { first with PrefixLen = newP; KeySuffix = newSuffix } 744 newFirst :: rs 745 746 before @ [ newPrevE ] @ newRest 747 748 let newLeft = if foundIdx = 0 then Some mergedChildCid else node.Left 749 750 let newNode = { Left = newLeft; Entries = newEntries } 751 752 if newNode.Entries.IsEmpty && newNode.Left.IsNone then 753 return None 754 else 755 return Some newNode 756 757 else 758 let childCid = 759 if nextTreeIdx = -1 then 760 node.Left 761 else 762 node.Entries.[nextTreeIdx].Tree 763 764 match childCid with 765 | None -> return Some node 766 | Some cid -> 767 let! childNodeOpt = loader cid 768 769 match childNodeOpt with 770 | None -> return Some node 771 | Some childNode -> 772 let! newChildOpt = delete loader persister childNode key currentKey 773 774 match newChildOpt with 775 | None -> 776 if nextTreeIdx = -1 then 777 return Some { node with Left = None } 778 else 779 let prevE = node.Entries.[nextTreeIdx] 780 let newPrevE = { prevE with Tree = None } 781 782 let newEntries = 783 node.Entries 784 |> List.mapi (fun idx x -> if idx = nextTreeIdx then newPrevE else x) 785 786 return Some { node with Entries = newEntries } 787 | Some newChild -> 788 let! newChildCid = persister newChild 789 790 if nextTreeIdx = -1 then 791 return Some { node with Left = Some newChildCid } 792 else 793 let prevE = node.Entries.[nextTreeIdx] 794 let newPrevE = { prevE with Tree = Some newChildCid } 795 796 let newEntries = 797 node.Entries 798 |> List.mapi (fun idx x -> if idx = nextTreeIdx then newPrevE else x) 799 800 return Some { node with Entries = newEntries } 801 } 802 803 let fromEntries (loader : NodeLoader) (persister : NodePersister) (entries : (string * Cid) list) : Async<MstNode> = async { 804 let mutable root = { Left = None; Entries = [] } 805 let sortedEntries = entries |> List.sortBy fst 806 807 for k, v in sortedEntries do 808 let! newRoot = put loader persister root k v "" 809 root <- newRoot 810 811 return root 812 }