github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/allegrosql/memdb.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package ekv 15 16 import ( 17 "bytes" 18 "context" 19 "reflect" 20 "sync" 21 "sync/atomic" 22 "unsafe" 23 ) 24 25 const ( 26 flagPresumeKNE KeyFlags = 1 << iota 27 flagKeyLocked 28 flagKeyLockedValExist 29 flagNeedCheckExists 30 flagNoNeedCommit 31 32 persistentFlags = flagKeyLocked | flagKeyLockedValExist 33 // bit 1 => red, bit 0 => black 34 nodeDefCausorBit uint8 = 0x80 35 nodeFlagsMask = ^nodeDefCausorBit 36 ) 37 38 // KeyFlags are spacetimedata associated with key 39 type KeyFlags uint8 40 41 // HasPresumeKeyNotExists returns whether the associated key use lazy check. 42 func (f KeyFlags) HasPresumeKeyNotExists() bool { 43 return f&flagPresumeKNE != 0 44 } 45 46 // HasLocked returns whether the associated key has acquired pessimistic dagger. 47 func (f KeyFlags) HasLocked() bool { 48 return f&flagKeyLocked != 0 49 } 50 51 // HasLockedValueExists returns whether the value exists when key locked. 52 func (f KeyFlags) HasLockedValueExists() bool { 53 return f&flagKeyLockedValExist != 0 54 } 55 56 // HasNeedCheckExists returns whether the key need to check existence when it has been locked. 57 func (f KeyFlags) HasNeedCheckExists() bool { 58 return f&flagNeedCheckExists != 0 59 } 60 61 // HasNoNeedCommit returns whether the key should be used in 2pc commit phase. 62 func (f KeyFlags) HasNoNeedCommit() bool { 63 return f&flagNoNeedCommit != 0 64 } 65 66 // FlagsOp describes KeyFlags modify operation. 67 type FlagsOp uint16 68 69 const ( 70 // SetPresumeKeyNotExists marks the existence of the associated key is checked lazily. 71 // Implies KeyFlags.HasNeedCheckExists() == true. 72 SetPresumeKeyNotExists FlagsOp = 1 << iota 73 // DelPresumeKeyNotExists reverts SetPresumeKeyNotExists. 74 DelPresumeKeyNotExists 75 // SetKeyLocked marks the associated key has acquired dagger. 76 SetKeyLocked 77 // DelKeyLocked reverts SetKeyLocked. 78 DelKeyLocked 79 // SetKeyLockedValueExists marks the value exists when key has been locked in Transaction.LockKeys. 80 SetKeyLockedValueExists 81 // SetKeyLockedValueNotExists marks the value doesn't exists when key has been locked in Transaction.LockKeys. 82 SetKeyLockedValueNotExists 83 // DelNeedCheckExists marks the key no need to be checked in Transaction.LockKeys. 84 DelNeedCheckExists 85 // SetNoNeedCommit marks the key shouldn't be used in 2pc commit phase. 86 SetNoNeedCommit 87 ) 88 89 func applyFlagsOps(origin KeyFlags, ops ...FlagsOp) KeyFlags { 90 for _, op := range ops { 91 switch op { 92 case SetPresumeKeyNotExists: 93 origin |= flagPresumeKNE | flagNeedCheckExists 94 case DelPresumeKeyNotExists: 95 origin &= ^(flagPresumeKNE | flagNeedCheckExists) 96 case SetKeyLocked: 97 origin |= flagKeyLocked 98 case DelKeyLocked: 99 origin &= ^flagKeyLocked 100 case SetKeyLockedValueExists: 101 origin |= flagKeyLockedValExist 102 case DelNeedCheckExists: 103 origin &= ^flagNeedCheckExists 104 case SetKeyLockedValueNotExists: 105 origin &= ^flagKeyLockedValExist 106 case SetNoNeedCommit: 107 origin |= flagNoNeedCommit 108 } 109 } 110 return origin 111 } 112 113 var tombstone = []byte{} 114 115 // memdb is rollbackable Red-Black Tree optimized for MilevaDB's transaction states buffer use scenario. 116 // You can think memdb is a combination of two separate tree map, one for key => value and another for key => keyFlags. 117 // 118 // The value map is rollbackable, that means you can use the `Staging`, `Release` and `Cleanup` API to safely modify KVs. 119 // 120 // The flags map is not rollbackable. There are two types of flag, persistent and non-persistent. 121 // When discarding a newly added KV in `Cleanup`, the non-persistent flags will be cleared. 122 // If there are persistent flags associated with key, we will keep this key in node without value. 123 type memdb struct { 124 // This RWMutex only used to ensure memdbSnapGetter.Get will not race with 125 // concurrent memdb.Set, memdb.SetWithFlags, memdb.Delete and memdb.UFIDelateFlags. 126 sync.RWMutex 127 root memdbMemCamAddr 128 allocator nodeSlabPredictor 129 vlog memdbVlog 130 131 entrySizeLimit uint64 132 bufferSizeLimit uint64 133 count int 134 size int 135 136 vlogInvalid bool 137 dirty bool 138 stages []memdbCheckpoint 139 } 140 141 func newMemDB() *memdb { 142 EDB := new(memdb) 143 EDB.allocator.init() 144 EDB.root = nullAddr 145 EDB.stages = make([]memdbCheckpoint, 0, 2) 146 EDB.entrySizeLimit = atomic.LoadUint64(&TxnEntrySizeLimit) 147 EDB.bufferSizeLimit = atomic.LoadUint64(&TxnTotalSizeLimit) 148 return EDB 149 } 150 151 func (EDB *memdb) Staging() StagingHandle { 152 EDB.Lock() 153 defer EDB.Unlock() 154 155 EDB.stages = append(EDB.stages, EDB.vlog.checkpoint()) 156 return StagingHandle(len(EDB.stages)) 157 } 158 159 func (EDB *memdb) Release(h StagingHandle) { 160 if int(h) != len(EDB.stages) { 161 // This should never happens in production environment. 162 // Use panic to make debug easier. 163 panic("cannot release staging buffer") 164 } 165 166 EDB.Lock() 167 defer EDB.Unlock() 168 if int(h) == 1 { 169 tail := EDB.vlog.checkpoint() 170 if !EDB.stages[0].isSamePosition(&tail) { 171 EDB.dirty = true 172 } 173 } 174 EDB.stages = EDB.stages[:int(h)-1] 175 } 176 177 func (EDB *memdb) Cleanup(h StagingHandle) { 178 if int(h) > len(EDB.stages) { 179 return 180 } 181 if int(h) < len(EDB.stages) { 182 // This should never happens in production environment. 183 // Use panic to make debug easier. 184 panic("cannot cleanup staging buffer") 185 } 186 187 EDB.Lock() 188 defer EDB.Unlock() 189 cp := &EDB.stages[int(h)-1] 190 if !EDB.vlogInvalid { 191 EDB.vlog.revertToCheckpoint(EDB, cp) 192 EDB.vlog.truncate(cp) 193 } 194 EDB.stages = EDB.stages[:int(h)-1] 195 } 196 197 func (EDB *memdb) Reset() { 198 EDB.root = nullAddr 199 EDB.stages = EDB.stages[:0] 200 EDB.dirty = false 201 EDB.vlogInvalid = false 202 EDB.size = 0 203 EDB.count = 0 204 EDB.vlog.reset() 205 EDB.allocator.reset() 206 } 207 208 func (EDB *memdb) DiscardValues() { 209 EDB.vlogInvalid = true 210 EDB.vlog.reset() 211 } 212 213 func (EDB *memdb) InspectStage(handle StagingHandle, f func(Key, KeyFlags, []byte)) { 214 idx := int(handle) - 1 215 tail := EDB.vlog.checkpoint() 216 head := EDB.stages[idx] 217 EDB.vlog.inspectKVInLog(EDB, &head, &tail, f) 218 } 219 220 func (EDB *memdb) Get(_ context.Context, key Key) ([]byte, error) { 221 if EDB.vlogInvalid { 222 // panic for easier debugging. 223 panic("vlog is resetted") 224 } 225 226 x := EDB.traverse(key, false) 227 if x.isNull() { 228 return nil, ErrNotExist 229 } 230 if x.vptr.isNull() { 231 // A flag only key, act as value not exists 232 return nil, ErrNotExist 233 } 234 return EDB.vlog.getValue(x.vptr), nil 235 } 236 237 func (EDB *memdb) GetFlags(key Key) (KeyFlags, error) { 238 x := EDB.traverse(key, false) 239 if x.isNull() { 240 return 0, ErrNotExist 241 } 242 return x.getKeyFlags(), nil 243 } 244 245 func (EDB *memdb) UFIDelateFlags(key Key, ops ...FlagsOp) { 246 err := EDB.set(key, nil, ops...) 247 _ = err // set without value will never fail 248 } 249 250 func (EDB *memdb) Set(key Key, value []byte) error { 251 if len(value) == 0 { 252 return ErrCannotSetNilValue 253 } 254 return EDB.set(key, value) 255 } 256 257 func (EDB *memdb) SetWithFlags(key Key, value []byte, ops ...FlagsOp) error { 258 if len(value) == 0 { 259 return ErrCannotSetNilValue 260 } 261 return EDB.set(key, value, ops...) 262 } 263 264 func (EDB *memdb) Delete(key Key) error { 265 return EDB.set(key, tombstone) 266 } 267 268 func (EDB *memdb) Len() int { 269 return EDB.count 270 } 271 272 func (EDB *memdb) Size() int { 273 return EDB.size 274 } 275 276 func (EDB *memdb) Dirty() bool { 277 return EDB.dirty 278 } 279 280 func (EDB *memdb) set(key Key, value []byte, ops ...FlagsOp) error { 281 if EDB.vlogInvalid { 282 // panic for easier debugging. 283 panic("vlog is resetted") 284 } 285 286 if value != nil { 287 if size := uint64(len(key) + len(value)); size > EDB.entrySizeLimit { 288 return ErrEntryTooLarge.GenWithStackByArgs(EDB.entrySizeLimit, size) 289 } 290 } 291 292 EDB.Lock() 293 defer EDB.Unlock() 294 295 if len(EDB.stages) == 0 { 296 EDB.dirty = true 297 } 298 x := EDB.traverse(key, true) 299 300 if len(ops) != 0 { 301 flags := applyFlagsOps(x.getKeyFlags(), ops...) 302 if flags&persistentFlags != 0 { 303 EDB.dirty = true 304 } 305 x.setKeyFlags(flags) 306 } 307 308 if value == nil { 309 return nil 310 } 311 312 EDB.setValue(x, value) 313 if uint64(EDB.Size()) > EDB.bufferSizeLimit { 314 return ErrTxnTooLarge.GenWithStackByArgs(EDB.Size()) 315 } 316 return nil 317 } 318 319 func (EDB *memdb) setValue(x memdbNodeAddr, value []byte) { 320 var activeCp *memdbCheckpoint 321 if len(EDB.stages) > 0 { 322 activeCp = &EDB.stages[len(EDB.stages)-1] 323 } 324 325 var oldVal []byte 326 if !x.vptr.isNull() { 327 oldVal = EDB.vlog.getValue(x.vptr) 328 } 329 330 if len(oldVal) > 0 && EDB.vlog.canModify(activeCp, x.vptr) { 331 // For easier to implement, we only consider this case. 332 // It is the most common usage in MilevaDB's transaction buffers. 333 if len(oldVal) == len(value) { 334 copy(oldVal, value) 335 return 336 } 337 } 338 x.vptr = EDB.vlog.appendValue(x.addr, x.vptr, value) 339 EDB.size = EDB.size - len(oldVal) + len(value) 340 } 341 342 // traverse search for and if not found and insert is true, will add a new node in. 343 // Returns a pointer to the new node, or the node found. 344 func (EDB *memdb) traverse(key Key, insert bool) memdbNodeAddr { 345 x := EDB.getRoot() 346 y := memdbNodeAddr{nil, nullAddr} 347 found := false 348 349 // walk x down the tree 350 for !x.isNull() && !found { 351 y = x 352 cmp := bytes.Compare(key, x.getKey()) 353 if cmp < 0 { 354 x = x.getLeft(EDB) 355 } else if cmp > 0 { 356 x = x.getRight(EDB) 357 } else { 358 found = true 359 } 360 } 361 362 if found || !insert { 363 return x 364 } 365 366 z := EDB.allocNode(key) 367 z.up = y.addr 368 369 if y.isNull() { 370 EDB.root = z.addr 371 } else { 372 cmp := bytes.Compare(z.getKey(), y.getKey()) 373 if cmp < 0 { 374 y.left = z.addr 375 } else { 376 y.right = z.addr 377 } 378 } 379 380 z.left = nullAddr 381 z.right = nullAddr 382 383 // colour this new node red 384 z.setRed() 385 386 // Having added a red node, we must now walk back up the tree balancing it, 387 // by a series of rotations and changing of colours 388 x = z 389 390 // While we are not at the top and our parent node is red 391 // NOTE: Since the root node is guaranteed black, then we 392 // are also going to stop if we are the child of the root 393 394 for x.addr != EDB.root { 395 xUp := x.getUp(EDB) 396 if xUp.isBlack() { 397 break 398 } 399 400 xUpUp := xUp.getUp(EDB) 401 // if our parent is on the left side of our grandparent 402 if x.up == xUpUp.left { 403 // get the right side of our grandparent (uncle?) 404 y = xUpUp.getRight(EDB) 405 if y.isRed() { 406 // make our parent black 407 xUp.setBlack() 408 // make our uncle black 409 y.setBlack() 410 // make our grandparent red 411 xUpUp.setRed() 412 // now consider our grandparent 413 x = xUp.getUp(EDB) 414 } else { 415 // if we are on the right side of our parent 416 if x.addr == xUp.right { 417 // Move up to our parent 418 x = x.getUp(EDB) 419 EDB.leftRotate(x) 420 xUp = x.getUp(EDB) 421 xUpUp = xUp.getUp(EDB) 422 } 423 424 xUp.setBlack() 425 xUpUp.setRed() 426 EDB.rightRotate(xUpUp) 427 } 428 } else { 429 // everything here is the same as above, but exchanging left for right 430 y = xUpUp.getLeft(EDB) 431 if y.isRed() { 432 xUp.setBlack() 433 y.setBlack() 434 xUpUp.setRed() 435 436 x = xUp.getUp(EDB) 437 } else { 438 if x.addr == xUp.left { 439 x = x.getUp(EDB) 440 EDB.rightRotate(x) 441 xUp = x.getUp(EDB) 442 xUpUp = xUp.getUp(EDB) 443 } 444 445 xUp.setBlack() 446 xUpUp.setRed() 447 EDB.leftRotate(xUpUp) 448 } 449 } 450 } 451 452 // Set the root node black 453 EDB.getRoot().setBlack() 454 455 return z 456 } 457 458 // 459 // Rotate our tree thus:- 460 // 461 // X leftRotate(X)---> Y 462 // / \ / \ 463 // A Y <---rightRotate(Y) X C 464 // / \ / \ 465 // B C A B 466 // 467 // NOTE: This does not change the ordering. 468 // 469 // We assume that neither X nor Y is NULL 470 // 471 472 func (EDB *memdb) leftRotate(x memdbNodeAddr) { 473 y := x.getRight(EDB) 474 475 // Turn Y's left subtree into X's right subtree (move B) 476 x.right = y.left 477 478 // If B is not null, set it's parent to be X 479 if !y.left.isNull() { 480 left := y.getLeft(EDB) 481 left.up = x.addr 482 } 483 484 // Set Y's parent to be what X's parent was 485 y.up = x.up 486 487 // if X was the root 488 if x.up.isNull() { 489 EDB.root = y.addr 490 } else { 491 xUp := x.getUp(EDB) 492 // Set X's parent's left or right pointer to be Y 493 if x.addr == xUp.left { 494 xUp.left = y.addr 495 } else { 496 xUp.right = y.addr 497 } 498 } 499 500 // Put X on Y's left 501 y.left = x.addr 502 // Set X's parent to be Y 503 x.up = y.addr 504 } 505 506 func (EDB *memdb) rightRotate(y memdbNodeAddr) { 507 x := y.getLeft(EDB) 508 509 // Turn X's right subtree into Y's left subtree (move B) 510 y.left = x.right 511 512 // If B is not null, set it's parent to be Y 513 if !x.right.isNull() { 514 right := x.getRight(EDB) 515 right.up = y.addr 516 } 517 518 // Set X's parent to be what Y's parent was 519 x.up = y.up 520 521 // if Y was the root 522 if y.up.isNull() { 523 EDB.root = x.addr 524 } else { 525 yUp := y.getUp(EDB) 526 // Set Y's parent's left or right pointer to be X 527 if y.addr == yUp.left { 528 yUp.left = x.addr 529 } else { 530 yUp.right = x.addr 531 } 532 } 533 534 // Put Y on X's right 535 x.right = y.addr 536 // Set Y's parent to be X 537 y.up = x.addr 538 } 539 540 func (EDB *memdb) deleteNode(z memdbNodeAddr) { 541 var x, y memdbNodeAddr 542 543 EDB.count-- 544 EDB.size -= int(z.klen) 545 546 if z.left.isNull() || z.right.isNull() { 547 y = z 548 } else { 549 y = EDB.successor(z) 550 } 551 552 if !y.left.isNull() { 553 x = y.getLeft(EDB) 554 } else { 555 x = y.getRight(EDB) 556 } 557 x.up = y.up 558 559 if y.up.isNull() { 560 EDB.root = x.addr 561 } else { 562 yUp := y.getUp(EDB) 563 if y.addr == yUp.left { 564 yUp.left = x.addr 565 } else { 566 yUp.right = x.addr 567 } 568 } 569 570 needFix := y.isBlack() 571 572 // NOTE: traditional red-black tree will copy key from Y to Z and free Y. 573 // We cannot do the same thing here, due to Y's pointer is stored in vlog and the space in Z may not suiblock for Y. 574 // So we need to copy states from Z to Y, and relink all nodes formerly connected to Z. 575 if y != z { 576 EDB.replaceNode(z, y) 577 } 578 579 if needFix { 580 EDB.deleteNodeFix(x) 581 } 582 583 EDB.allocator.freeNode(z.addr) 584 } 585 586 func (EDB *memdb) replaceNode(old memdbNodeAddr, new memdbNodeAddr) { 587 if !old.up.isNull() { 588 oldUp := old.getUp(EDB) 589 if old.addr == oldUp.left { 590 oldUp.left = new.addr 591 } else { 592 oldUp.right = new.addr 593 } 594 } else { 595 EDB.root = new.addr 596 } 597 new.up = old.up 598 599 left := old.getLeft(EDB) 600 left.up = new.addr 601 new.left = old.left 602 603 right := old.getRight(EDB) 604 right.up = new.addr 605 new.right = old.right 606 607 if old.isBlack() { 608 new.setBlack() 609 } else { 610 new.setRed() 611 } 612 } 613 614 func (EDB *memdb) deleteNodeFix(x memdbNodeAddr) { 615 for x.addr != EDB.root && x.isBlack() { 616 xUp := x.getUp(EDB) 617 if x.addr == xUp.left { 618 w := xUp.getRight(EDB) 619 if w.isRed() { 620 w.setBlack() 621 xUp.setRed() 622 EDB.leftRotate(xUp) 623 w = x.getUp(EDB).getRight(EDB) 624 } 625 626 if w.getLeft(EDB).isBlack() && w.getRight(EDB).isBlack() { 627 w.setRed() 628 x = x.getUp(EDB) 629 } else { 630 if w.getRight(EDB).isBlack() { 631 w.getLeft(EDB).setBlack() 632 w.setRed() 633 EDB.rightRotate(w) 634 w = x.getUp(EDB).getRight(EDB) 635 } 636 637 xUp := x.getUp(EDB) 638 if xUp.isBlack() { 639 w.setBlack() 640 } else { 641 w.setRed() 642 } 643 xUp.setBlack() 644 w.getRight(EDB).setBlack() 645 EDB.leftRotate(xUp) 646 x = EDB.getRoot() 647 } 648 } else { 649 w := xUp.getLeft(EDB) 650 if w.isRed() { 651 w.setBlack() 652 xUp.setRed() 653 EDB.rightRotate(xUp) 654 w = x.getUp(EDB).getLeft(EDB) 655 } 656 657 if w.getRight(EDB).isBlack() && w.getLeft(EDB).isBlack() { 658 w.setRed() 659 x = x.getUp(EDB) 660 } else { 661 if w.getLeft(EDB).isBlack() { 662 w.getRight(EDB).setBlack() 663 w.setRed() 664 EDB.leftRotate(w) 665 w = x.getUp(EDB).getLeft(EDB) 666 } 667 668 xUp := x.getUp(EDB) 669 if xUp.isBlack() { 670 w.setBlack() 671 } else { 672 w.setRed() 673 } 674 xUp.setBlack() 675 w.getLeft(EDB).setBlack() 676 EDB.rightRotate(xUp) 677 x = EDB.getRoot() 678 } 679 } 680 } 681 x.setBlack() 682 } 683 684 func (EDB *memdb) successor(x memdbNodeAddr) (y memdbNodeAddr) { 685 if !x.right.isNull() { 686 // If right is not NULL then go right one and 687 // then keep going left until we find a node with 688 // no left pointer. 689 690 y = x.getRight(EDB) 691 for !y.left.isNull() { 692 y = y.getLeft(EDB) 693 } 694 return 695 } 696 697 // Go up the tree until we get to a node that is on the 698 // left of its parent (or the root) and then return the 699 // parent. 700 701 y = x.getUp(EDB) 702 for !y.isNull() && x.addr == y.right { 703 x = y 704 y = y.getUp(EDB) 705 } 706 return y 707 } 708 709 func (EDB *memdb) predecessor(x memdbNodeAddr) (y memdbNodeAddr) { 710 if !x.left.isNull() { 711 // If left is not NULL then go left one and 712 // then keep going right until we find a node with 713 // no right pointer. 714 715 y = x.getLeft(EDB) 716 for !y.right.isNull() { 717 y = y.getRight(EDB) 718 } 719 return 720 } 721 722 // Go up the tree until we get to a node that is on the 723 // right of its parent (or the root) and then return the 724 // parent. 725 726 y = x.getUp(EDB) 727 for !y.isNull() && x.addr == y.left { 728 x = y 729 y = y.getUp(EDB) 730 } 731 return y 732 } 733 734 func (EDB *memdb) getNode(x memdbMemCamAddr) memdbNodeAddr { 735 return memdbNodeAddr{EDB.allocator.getNode(x), x} 736 } 737 738 func (EDB *memdb) getRoot() memdbNodeAddr { 739 return EDB.getNode(EDB.root) 740 } 741 742 func (EDB *memdb) allocNode(key Key) memdbNodeAddr { 743 EDB.size += len(key) 744 EDB.count++ 745 x, xn := EDB.allocator.allocNode(key) 746 return memdbNodeAddr{xn, x} 747 } 748 749 type memdbNodeAddr struct { 750 *memdbNode 751 addr memdbMemCamAddr 752 } 753 754 func (a *memdbNodeAddr) isNull() bool { 755 return a.addr.isNull() 756 } 757 758 func (a memdbNodeAddr) getUp(EDB *memdb) memdbNodeAddr { 759 return EDB.getNode(a.up) 760 } 761 762 func (a memdbNodeAddr) getLeft(EDB *memdb) memdbNodeAddr { 763 return EDB.getNode(a.left) 764 } 765 766 func (a memdbNodeAddr) getRight(EDB *memdb) memdbNodeAddr { 767 return EDB.getNode(a.right) 768 } 769 770 type memdbNode struct { 771 up memdbMemCamAddr 772 left memdbMemCamAddr 773 right memdbMemCamAddr 774 vptr memdbMemCamAddr 775 klen uint16 776 flags uint8 777 } 778 779 func (n *memdbNode) isRed() bool { 780 return n.flags&nodeDefCausorBit != 0 781 } 782 783 func (n *memdbNode) isBlack() bool { 784 return !n.isRed() 785 } 786 787 func (n *memdbNode) setRed() { 788 n.flags |= nodeDefCausorBit 789 } 790 791 func (n *memdbNode) setBlack() { 792 n.flags &= ^nodeDefCausorBit 793 } 794 795 func (n *memdbNode) getKey() Key { 796 var ret []byte 797 hdr := (*reflect.SliceHeader)(unsafe.Pointer(&ret)) 798 hdr.Data = uintptr(unsafe.Pointer(&n.flags)) + 1 799 hdr.Len = int(n.klen) 800 hdr.Cap = int(n.klen) 801 return ret 802 } 803 804 func (n *memdbNode) getKeyFlags() KeyFlags { 805 return KeyFlags(n.flags & nodeFlagsMask) 806 } 807 808 func (n *memdbNode) setKeyFlags(f KeyFlags) { 809 n.flags = (^nodeFlagsMask & n.flags) | uint8(f) 810 }