github.com/apache/arrow/go/v14@v14.0.2/internal/hashing/xxh3_memo_table.gen.go (about) 1 // Code generated by xxh3_memo_table.gen.go.tmpl. DO NOT EDIT. 2 3 // Licensed to the Apache Software Foundation (ASF) under one 4 // or more contributor license agreements. See the NOTICE file 5 // distributed with this work for additional information 6 // regarding copyright ownership. The ASF licenses this file 7 // to you under the Apache License, Version 2.0 (the 8 // "License"); you may not use this file except in compliance 9 // with the License. You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 19 package hashing 20 21 import ( 22 "math" 23 24 "github.com/apache/arrow/go/v14/arrow" 25 "github.com/apache/arrow/go/v14/arrow/bitutil" 26 "github.com/apache/arrow/go/v14/internal/utils" 27 ) 28 29 type payloadInt8 struct { 30 val int8 31 memoIdx int32 32 } 33 34 type entryInt8 struct { 35 h uint64 36 payload payloadInt8 37 } 38 39 func (e entryInt8) Valid() bool { return e.h != sentinel } 40 41 // Int8HashTable is a hashtable specifically for int8 that 42 // is utilized with the MemoTable to generalize interactions for easier 43 // implementation of dictionaries without losing performance. 44 type Int8HashTable struct { 45 cap uint64 46 capMask uint64 47 size uint64 48 49 entries []entryInt8 50 } 51 52 // NewInt8HashTable returns a new hash table for int8 values 53 // initialized with the passed in capacity or 32 whichever is larger. 54 func NewInt8HashTable(cap uint64) *Int8HashTable { 55 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 56 ret := &Int8HashTable{cap: initCap, capMask: initCap - 1, size: 0} 57 ret.entries = make([]entryInt8, initCap) 58 return ret 59 } 60 61 // Reset drops all of the values in this hash table and re-initializes it 62 // with the specified initial capacity as if by calling New, but without having 63 // to reallocate the object. 64 func (h *Int8HashTable) Reset(cap uint64) { 65 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 66 h.capMask = h.cap - 1 67 h.size = 0 68 h.entries = make([]entryInt8, h.cap) 69 } 70 71 // CopyValues is used for copying the values out of the hash table into the 72 // passed in slice, in the order that they were first inserted 73 func (h *Int8HashTable) CopyValues(out []int8) { 74 h.CopyValuesSubset(0, out) 75 } 76 77 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 78 // with the value at start, in the order that they were inserted. 79 func (h *Int8HashTable) CopyValuesSubset(start int, out []int8) { 80 h.VisitEntries(func(e *entryInt8) { 81 idx := e.payload.memoIdx - int32(start) 82 if idx >= 0 { 83 out[idx] = e.payload.val 84 } 85 }) 86 } 87 88 func (h *Int8HashTable) WriteOut(out []byte) { 89 h.WriteOutSubset(0, out) 90 } 91 92 func (h *Int8HashTable) WriteOutSubset(start int, out []byte) { 93 data := arrow.Int8Traits.CastFromBytes(out) 94 h.VisitEntries(func(e *entryInt8) { 95 idx := e.payload.memoIdx - int32(start) 96 if idx >= 0 { 97 data[idx] = e.payload.val 98 } 99 }) 100 } 101 102 func (h *Int8HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 103 104 func (Int8HashTable) fixHash(v uint64) uint64 { 105 if v == sentinel { 106 return 42 107 } 108 return v 109 } 110 111 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 112 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 113 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 114 func (h *Int8HashTable) Lookup(v uint64, cmp func(int8) bool) (*entryInt8, bool) { 115 idx, ok := h.lookup(v, h.capMask, cmp) 116 return &h.entries[idx], ok 117 } 118 119 func (h *Int8HashTable) lookup(v uint64, szMask uint64, cmp func(int8) bool) (uint64, bool) { 120 const perturbShift uint8 = 5 121 122 var ( 123 idx uint64 124 perturb uint64 125 e *entryInt8 126 ) 127 128 v = h.fixHash(v) 129 idx = v & szMask 130 perturb = (v >> uint64(perturbShift)) + 1 131 132 for { 133 e = &h.entries[idx] 134 if e.h == v && cmp(e.payload.val) { 135 return idx, true 136 } 137 138 if e.h == sentinel { 139 return idx, false 140 } 141 142 // perturbation logic inspired from CPython's set/dict object 143 // the goal is that all 64 bits of unmasked hash value eventually 144 // participate int he probing sequence, to minimize clustering 145 idx = (idx + perturb) & szMask 146 perturb = (perturb >> uint64(perturbShift)) + 1 147 } 148 } 149 150 func (h *Int8HashTable) upsize(newcap uint64) error { 151 newMask := newcap - 1 152 153 oldEntries := h.entries 154 h.entries = make([]entryInt8, newcap) 155 for _, e := range oldEntries { 156 if e.Valid() { 157 idx, _ := h.lookup(e.h, newMask, func(int8) bool { return false }) 158 h.entries[idx] = e 159 } 160 } 161 h.cap = newcap 162 h.capMask = newMask 163 return nil 164 } 165 166 // Insert updates the given entry with the provided hash value, payload value and memo index. 167 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 168 func (h *Int8HashTable) Insert(e *entryInt8, v uint64, val int8, memoIdx int32) error { 169 e.h = h.fixHash(v) 170 e.payload.val = val 171 e.payload.memoIdx = memoIdx 172 h.size++ 173 174 if h.needUpsize() { 175 h.upsize(h.cap * uint64(loadFactor) * 2) 176 } 177 return nil 178 } 179 180 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 181 // a valid entry being one which has had a value inserted into it. 182 func (h *Int8HashTable) VisitEntries(visit func(*entryInt8)) { 183 for _, e := range h.entries { 184 if e.Valid() { 185 visit(&e) 186 } 187 } 188 } 189 190 // Int8MemoTable is a wrapper over the appropriate hashtable to provide an interface 191 // conforming to the MemoTable interface defined in the encoding package for general interactions 192 // regarding dictionaries. 193 type Int8MemoTable struct { 194 tbl *Int8HashTable 195 nullIdx int32 196 } 197 198 // NewInt8MemoTable returns a new memotable with num entries pre-allocated to reduce further 199 // allocations when inserting. 200 func NewInt8MemoTable(num int64) *Int8MemoTable { 201 return &Int8MemoTable{tbl: NewInt8HashTable(uint64(num)), nullIdx: KeyNotFound} 202 } 203 204 func (Int8MemoTable) TypeTraits() TypeTraits { 205 return arrow.Int8Traits 206 } 207 208 // Reset allows this table to be re-used by dumping all the data currently in the table. 209 func (s *Int8MemoTable) Reset() { 210 s.tbl.Reset(32) 211 s.nullIdx = KeyNotFound 212 } 213 214 // Size returns the current number of inserted elements into the table including if a null 215 // has been inserted. 216 func (s *Int8MemoTable) Size() int { 217 sz := int(s.tbl.size) 218 if _, ok := s.GetNull(); ok { 219 sz++ 220 } 221 return sz 222 } 223 224 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 225 // that will be true if found and false if not. 226 func (s *Int8MemoTable) GetNull() (int, bool) { 227 return int(s.nullIdx), s.nullIdx != KeyNotFound 228 } 229 230 // GetOrInsertNull will return the index of the null entry or insert a null entry 231 // if one currently doesn't exist. The found value will be true if there was already 232 // a null in the table, and false if it inserted one. 233 func (s *Int8MemoTable) GetOrInsertNull() (idx int, found bool) { 234 idx, found = s.GetNull() 235 if !found { 236 idx = s.Size() 237 s.nullIdx = int32(idx) 238 } 239 return 240 } 241 242 // CopyValues will copy the values from the memo table out into the passed in slice 243 // which must be of the appropriate type. 244 func (s *Int8MemoTable) CopyValues(out interface{}) { 245 s.CopyValuesSubset(0, out) 246 } 247 248 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 249 // at the provided start index 250 func (s *Int8MemoTable) CopyValuesSubset(start int, out interface{}) { 251 s.tbl.CopyValuesSubset(start, out.([]int8)) 252 } 253 254 func (s *Int8MemoTable) WriteOut(out []byte) { 255 s.tbl.CopyValues(arrow.Int8Traits.CastFromBytes(out)) 256 } 257 258 func (s *Int8MemoTable) WriteOutSubset(start int, out []byte) { 259 s.tbl.CopyValuesSubset(start, arrow.Int8Traits.CastFromBytes(out)) 260 } 261 262 func (s *Int8MemoTable) WriteOutLE(out []byte) { 263 s.tbl.WriteOut(out) 264 } 265 266 func (s *Int8MemoTable) WriteOutSubsetLE(start int, out []byte) { 267 s.tbl.WriteOutSubset(start, out) 268 } 269 270 // Get returns the index of the requested value in the hash table or KeyNotFound 271 // along with a boolean indicating if it was found or not. 272 func (s *Int8MemoTable) Get(val interface{}) (int, bool) { 273 274 h := hashInt(uint64(val.(int8)), 0) 275 if e, ok := s.tbl.Lookup(h, func(v int8) bool { return val.(int8) == v }); ok { 276 return int(e.payload.memoIdx), ok 277 } 278 return KeyNotFound, false 279 } 280 281 // GetOrInsert will return the index of the specified value in the table, or insert the 282 // value into the table and return the new index. found indicates whether or not it already 283 // existed in the table (true) or was inserted by this call (false). 284 func (s *Int8MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 285 286 h := hashInt(uint64(val.(int8)), 0) 287 e, ok := s.tbl.Lookup(h, func(v int8) bool { 288 return val.(int8) == v 289 }) 290 291 if ok { 292 idx = int(e.payload.memoIdx) 293 found = true 294 } else { 295 idx = s.Size() 296 s.tbl.Insert(e, h, val.(int8), int32(idx)) 297 } 298 return 299 } 300 301 // GetOrInsertBytes is unimplemented 302 func (s *Int8MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 303 panic("unimplemented") 304 } 305 306 type payloadUint8 struct { 307 val uint8 308 memoIdx int32 309 } 310 311 type entryUint8 struct { 312 h uint64 313 payload payloadUint8 314 } 315 316 func (e entryUint8) Valid() bool { return e.h != sentinel } 317 318 // Uint8HashTable is a hashtable specifically for uint8 that 319 // is utilized with the MemoTable to generalize interactions for easier 320 // implementation of dictionaries without losing performance. 321 type Uint8HashTable struct { 322 cap uint64 323 capMask uint64 324 size uint64 325 326 entries []entryUint8 327 } 328 329 // NewUint8HashTable returns a new hash table for uint8 values 330 // initialized with the passed in capacity or 32 whichever is larger. 331 func NewUint8HashTable(cap uint64) *Uint8HashTable { 332 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 333 ret := &Uint8HashTable{cap: initCap, capMask: initCap - 1, size: 0} 334 ret.entries = make([]entryUint8, initCap) 335 return ret 336 } 337 338 // Reset drops all of the values in this hash table and re-initializes it 339 // with the specified initial capacity as if by calling New, but without having 340 // to reallocate the object. 341 func (h *Uint8HashTable) Reset(cap uint64) { 342 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 343 h.capMask = h.cap - 1 344 h.size = 0 345 h.entries = make([]entryUint8, h.cap) 346 } 347 348 // CopyValues is used for copying the values out of the hash table into the 349 // passed in slice, in the order that they were first inserted 350 func (h *Uint8HashTable) CopyValues(out []uint8) { 351 h.CopyValuesSubset(0, out) 352 } 353 354 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 355 // with the value at start, in the order that they were inserted. 356 func (h *Uint8HashTable) CopyValuesSubset(start int, out []uint8) { 357 h.VisitEntries(func(e *entryUint8) { 358 idx := e.payload.memoIdx - int32(start) 359 if idx >= 0 { 360 out[idx] = e.payload.val 361 } 362 }) 363 } 364 365 func (h *Uint8HashTable) WriteOut(out []byte) { 366 h.WriteOutSubset(0, out) 367 } 368 369 func (h *Uint8HashTable) WriteOutSubset(start int, out []byte) { 370 data := arrow.Uint8Traits.CastFromBytes(out) 371 h.VisitEntries(func(e *entryUint8) { 372 idx := e.payload.memoIdx - int32(start) 373 if idx >= 0 { 374 data[idx] = e.payload.val 375 } 376 }) 377 } 378 379 func (h *Uint8HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 380 381 func (Uint8HashTable) fixHash(v uint64) uint64 { 382 if v == sentinel { 383 return 42 384 } 385 return v 386 } 387 388 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 389 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 390 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 391 func (h *Uint8HashTable) Lookup(v uint64, cmp func(uint8) bool) (*entryUint8, bool) { 392 idx, ok := h.lookup(v, h.capMask, cmp) 393 return &h.entries[idx], ok 394 } 395 396 func (h *Uint8HashTable) lookup(v uint64, szMask uint64, cmp func(uint8) bool) (uint64, bool) { 397 const perturbShift uint8 = 5 398 399 var ( 400 idx uint64 401 perturb uint64 402 e *entryUint8 403 ) 404 405 v = h.fixHash(v) 406 idx = v & szMask 407 perturb = (v >> uint64(perturbShift)) + 1 408 409 for { 410 e = &h.entries[idx] 411 if e.h == v && cmp(e.payload.val) { 412 return idx, true 413 } 414 415 if e.h == sentinel { 416 return idx, false 417 } 418 419 // perturbation logic inspired from CPython's set/dict object 420 // the goal is that all 64 bits of unmasked hash value eventually 421 // participate int he probing sequence, to minimize clustering 422 idx = (idx + perturb) & szMask 423 perturb = (perturb >> uint64(perturbShift)) + 1 424 } 425 } 426 427 func (h *Uint8HashTable) upsize(newcap uint64) error { 428 newMask := newcap - 1 429 430 oldEntries := h.entries 431 h.entries = make([]entryUint8, newcap) 432 for _, e := range oldEntries { 433 if e.Valid() { 434 idx, _ := h.lookup(e.h, newMask, func(uint8) bool { return false }) 435 h.entries[idx] = e 436 } 437 } 438 h.cap = newcap 439 h.capMask = newMask 440 return nil 441 } 442 443 // Insert updates the given entry with the provided hash value, payload value and memo index. 444 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 445 func (h *Uint8HashTable) Insert(e *entryUint8, v uint64, val uint8, memoIdx int32) error { 446 e.h = h.fixHash(v) 447 e.payload.val = val 448 e.payload.memoIdx = memoIdx 449 h.size++ 450 451 if h.needUpsize() { 452 h.upsize(h.cap * uint64(loadFactor) * 2) 453 } 454 return nil 455 } 456 457 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 458 // a valid entry being one which has had a value inserted into it. 459 func (h *Uint8HashTable) VisitEntries(visit func(*entryUint8)) { 460 for _, e := range h.entries { 461 if e.Valid() { 462 visit(&e) 463 } 464 } 465 } 466 467 // Uint8MemoTable is a wrapper over the appropriate hashtable to provide an interface 468 // conforming to the MemoTable interface defined in the encoding package for general interactions 469 // regarding dictionaries. 470 type Uint8MemoTable struct { 471 tbl *Uint8HashTable 472 nullIdx int32 473 } 474 475 // NewUint8MemoTable returns a new memotable with num entries pre-allocated to reduce further 476 // allocations when inserting. 477 func NewUint8MemoTable(num int64) *Uint8MemoTable { 478 return &Uint8MemoTable{tbl: NewUint8HashTable(uint64(num)), nullIdx: KeyNotFound} 479 } 480 481 func (Uint8MemoTable) TypeTraits() TypeTraits { 482 return arrow.Uint8Traits 483 } 484 485 // Reset allows this table to be re-used by dumping all the data currently in the table. 486 func (s *Uint8MemoTable) Reset() { 487 s.tbl.Reset(32) 488 s.nullIdx = KeyNotFound 489 } 490 491 // Size returns the current number of inserted elements into the table including if a null 492 // has been inserted. 493 func (s *Uint8MemoTable) Size() int { 494 sz := int(s.tbl.size) 495 if _, ok := s.GetNull(); ok { 496 sz++ 497 } 498 return sz 499 } 500 501 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 502 // that will be true if found and false if not. 503 func (s *Uint8MemoTable) GetNull() (int, bool) { 504 return int(s.nullIdx), s.nullIdx != KeyNotFound 505 } 506 507 // GetOrInsertNull will return the index of the null entry or insert a null entry 508 // if one currently doesn't exist. The found value will be true if there was already 509 // a null in the table, and false if it inserted one. 510 func (s *Uint8MemoTable) GetOrInsertNull() (idx int, found bool) { 511 idx, found = s.GetNull() 512 if !found { 513 idx = s.Size() 514 s.nullIdx = int32(idx) 515 } 516 return 517 } 518 519 // CopyValues will copy the values from the memo table out into the passed in slice 520 // which must be of the appropriate type. 521 func (s *Uint8MemoTable) CopyValues(out interface{}) { 522 s.CopyValuesSubset(0, out) 523 } 524 525 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 526 // at the provided start index 527 func (s *Uint8MemoTable) CopyValuesSubset(start int, out interface{}) { 528 s.tbl.CopyValuesSubset(start, out.([]uint8)) 529 } 530 531 func (s *Uint8MemoTable) WriteOut(out []byte) { 532 s.tbl.CopyValues(arrow.Uint8Traits.CastFromBytes(out)) 533 } 534 535 func (s *Uint8MemoTable) WriteOutSubset(start int, out []byte) { 536 s.tbl.CopyValuesSubset(start, arrow.Uint8Traits.CastFromBytes(out)) 537 } 538 539 func (s *Uint8MemoTable) WriteOutLE(out []byte) { 540 s.tbl.WriteOut(out) 541 } 542 543 func (s *Uint8MemoTable) WriteOutSubsetLE(start int, out []byte) { 544 s.tbl.WriteOutSubset(start, out) 545 } 546 547 // Get returns the index of the requested value in the hash table or KeyNotFound 548 // along with a boolean indicating if it was found or not. 549 func (s *Uint8MemoTable) Get(val interface{}) (int, bool) { 550 551 h := hashInt(uint64(val.(uint8)), 0) 552 if e, ok := s.tbl.Lookup(h, func(v uint8) bool { return val.(uint8) == v }); ok { 553 return int(e.payload.memoIdx), ok 554 } 555 return KeyNotFound, false 556 } 557 558 // GetOrInsert will return the index of the specified value in the table, or insert the 559 // value into the table and return the new index. found indicates whether or not it already 560 // existed in the table (true) or was inserted by this call (false). 561 func (s *Uint8MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 562 563 h := hashInt(uint64(val.(uint8)), 0) 564 e, ok := s.tbl.Lookup(h, func(v uint8) bool { 565 return val.(uint8) == v 566 }) 567 568 if ok { 569 idx = int(e.payload.memoIdx) 570 found = true 571 } else { 572 idx = s.Size() 573 s.tbl.Insert(e, h, val.(uint8), int32(idx)) 574 } 575 return 576 } 577 578 // GetOrInsertBytes is unimplemented 579 func (s *Uint8MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 580 panic("unimplemented") 581 } 582 583 type payloadInt16 struct { 584 val int16 585 memoIdx int32 586 } 587 588 type entryInt16 struct { 589 h uint64 590 payload payloadInt16 591 } 592 593 func (e entryInt16) Valid() bool { return e.h != sentinel } 594 595 // Int16HashTable is a hashtable specifically for int16 that 596 // is utilized with the MemoTable to generalize interactions for easier 597 // implementation of dictionaries without losing performance. 598 type Int16HashTable struct { 599 cap uint64 600 capMask uint64 601 size uint64 602 603 entries []entryInt16 604 } 605 606 // NewInt16HashTable returns a new hash table for int16 values 607 // initialized with the passed in capacity or 32 whichever is larger. 608 func NewInt16HashTable(cap uint64) *Int16HashTable { 609 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 610 ret := &Int16HashTable{cap: initCap, capMask: initCap - 1, size: 0} 611 ret.entries = make([]entryInt16, initCap) 612 return ret 613 } 614 615 // Reset drops all of the values in this hash table and re-initializes it 616 // with the specified initial capacity as if by calling New, but without having 617 // to reallocate the object. 618 func (h *Int16HashTable) Reset(cap uint64) { 619 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 620 h.capMask = h.cap - 1 621 h.size = 0 622 h.entries = make([]entryInt16, h.cap) 623 } 624 625 // CopyValues is used for copying the values out of the hash table into the 626 // passed in slice, in the order that they were first inserted 627 func (h *Int16HashTable) CopyValues(out []int16) { 628 h.CopyValuesSubset(0, out) 629 } 630 631 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 632 // with the value at start, in the order that they were inserted. 633 func (h *Int16HashTable) CopyValuesSubset(start int, out []int16) { 634 h.VisitEntries(func(e *entryInt16) { 635 idx := e.payload.memoIdx - int32(start) 636 if idx >= 0 { 637 out[idx] = e.payload.val 638 } 639 }) 640 } 641 642 func (h *Int16HashTable) WriteOut(out []byte) { 643 h.WriteOutSubset(0, out) 644 } 645 646 func (h *Int16HashTable) WriteOutSubset(start int, out []byte) { 647 data := arrow.Int16Traits.CastFromBytes(out) 648 h.VisitEntries(func(e *entryInt16) { 649 idx := e.payload.memoIdx - int32(start) 650 if idx >= 0 { 651 data[idx] = utils.ToLEInt16(e.payload.val) 652 } 653 }) 654 } 655 656 func (h *Int16HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 657 658 func (Int16HashTable) fixHash(v uint64) uint64 { 659 if v == sentinel { 660 return 42 661 } 662 return v 663 } 664 665 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 666 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 667 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 668 func (h *Int16HashTable) Lookup(v uint64, cmp func(int16) bool) (*entryInt16, bool) { 669 idx, ok := h.lookup(v, h.capMask, cmp) 670 return &h.entries[idx], ok 671 } 672 673 func (h *Int16HashTable) lookup(v uint64, szMask uint64, cmp func(int16) bool) (uint64, bool) { 674 const perturbShift uint8 = 5 675 676 var ( 677 idx uint64 678 perturb uint64 679 e *entryInt16 680 ) 681 682 v = h.fixHash(v) 683 idx = v & szMask 684 perturb = (v >> uint64(perturbShift)) + 1 685 686 for { 687 e = &h.entries[idx] 688 if e.h == v && cmp(e.payload.val) { 689 return idx, true 690 } 691 692 if e.h == sentinel { 693 return idx, false 694 } 695 696 // perturbation logic inspired from CPython's set/dict object 697 // the goal is that all 64 bits of unmasked hash value eventually 698 // participate int he probing sequence, to minimize clustering 699 idx = (idx + perturb) & szMask 700 perturb = (perturb >> uint64(perturbShift)) + 1 701 } 702 } 703 704 func (h *Int16HashTable) upsize(newcap uint64) error { 705 newMask := newcap - 1 706 707 oldEntries := h.entries 708 h.entries = make([]entryInt16, newcap) 709 for _, e := range oldEntries { 710 if e.Valid() { 711 idx, _ := h.lookup(e.h, newMask, func(int16) bool { return false }) 712 h.entries[idx] = e 713 } 714 } 715 h.cap = newcap 716 h.capMask = newMask 717 return nil 718 } 719 720 // Insert updates the given entry with the provided hash value, payload value and memo index. 721 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 722 func (h *Int16HashTable) Insert(e *entryInt16, v uint64, val int16, memoIdx int32) error { 723 e.h = h.fixHash(v) 724 e.payload.val = val 725 e.payload.memoIdx = memoIdx 726 h.size++ 727 728 if h.needUpsize() { 729 h.upsize(h.cap * uint64(loadFactor) * 2) 730 } 731 return nil 732 } 733 734 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 735 // a valid entry being one which has had a value inserted into it. 736 func (h *Int16HashTable) VisitEntries(visit func(*entryInt16)) { 737 for _, e := range h.entries { 738 if e.Valid() { 739 visit(&e) 740 } 741 } 742 } 743 744 // Int16MemoTable is a wrapper over the appropriate hashtable to provide an interface 745 // conforming to the MemoTable interface defined in the encoding package for general interactions 746 // regarding dictionaries. 747 type Int16MemoTable struct { 748 tbl *Int16HashTable 749 nullIdx int32 750 } 751 752 // NewInt16MemoTable returns a new memotable with num entries pre-allocated to reduce further 753 // allocations when inserting. 754 func NewInt16MemoTable(num int64) *Int16MemoTable { 755 return &Int16MemoTable{tbl: NewInt16HashTable(uint64(num)), nullIdx: KeyNotFound} 756 } 757 758 func (Int16MemoTable) TypeTraits() TypeTraits { 759 return arrow.Int16Traits 760 } 761 762 // Reset allows this table to be re-used by dumping all the data currently in the table. 763 func (s *Int16MemoTable) Reset() { 764 s.tbl.Reset(32) 765 s.nullIdx = KeyNotFound 766 } 767 768 // Size returns the current number of inserted elements into the table including if a null 769 // has been inserted. 770 func (s *Int16MemoTable) Size() int { 771 sz := int(s.tbl.size) 772 if _, ok := s.GetNull(); ok { 773 sz++ 774 } 775 return sz 776 } 777 778 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 779 // that will be true if found and false if not. 780 func (s *Int16MemoTable) GetNull() (int, bool) { 781 return int(s.nullIdx), s.nullIdx != KeyNotFound 782 } 783 784 // GetOrInsertNull will return the index of the null entry or insert a null entry 785 // if one currently doesn't exist. The found value will be true if there was already 786 // a null in the table, and false if it inserted one. 787 func (s *Int16MemoTable) GetOrInsertNull() (idx int, found bool) { 788 idx, found = s.GetNull() 789 if !found { 790 idx = s.Size() 791 s.nullIdx = int32(idx) 792 } 793 return 794 } 795 796 // CopyValues will copy the values from the memo table out into the passed in slice 797 // which must be of the appropriate type. 798 func (s *Int16MemoTable) CopyValues(out interface{}) { 799 s.CopyValuesSubset(0, out) 800 } 801 802 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 803 // at the provided start index 804 func (s *Int16MemoTable) CopyValuesSubset(start int, out interface{}) { 805 s.tbl.CopyValuesSubset(start, out.([]int16)) 806 } 807 808 func (s *Int16MemoTable) WriteOut(out []byte) { 809 s.tbl.CopyValues(arrow.Int16Traits.CastFromBytes(out)) 810 } 811 812 func (s *Int16MemoTable) WriteOutSubset(start int, out []byte) { 813 s.tbl.CopyValuesSubset(start, arrow.Int16Traits.CastFromBytes(out)) 814 } 815 816 func (s *Int16MemoTable) WriteOutLE(out []byte) { 817 s.tbl.WriteOut(out) 818 } 819 820 func (s *Int16MemoTable) WriteOutSubsetLE(start int, out []byte) { 821 s.tbl.WriteOutSubset(start, out) 822 } 823 824 // Get returns the index of the requested value in the hash table or KeyNotFound 825 // along with a boolean indicating if it was found or not. 826 func (s *Int16MemoTable) Get(val interface{}) (int, bool) { 827 828 h := hashInt(uint64(val.(int16)), 0) 829 if e, ok := s.tbl.Lookup(h, func(v int16) bool { return val.(int16) == v }); ok { 830 return int(e.payload.memoIdx), ok 831 } 832 return KeyNotFound, false 833 } 834 835 // GetOrInsert will return the index of the specified value in the table, or insert the 836 // value into the table and return the new index. found indicates whether or not it already 837 // existed in the table (true) or was inserted by this call (false). 838 func (s *Int16MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 839 840 h := hashInt(uint64(val.(int16)), 0) 841 e, ok := s.tbl.Lookup(h, func(v int16) bool { 842 return val.(int16) == v 843 }) 844 845 if ok { 846 idx = int(e.payload.memoIdx) 847 found = true 848 } else { 849 idx = s.Size() 850 s.tbl.Insert(e, h, val.(int16), int32(idx)) 851 } 852 return 853 } 854 855 // GetOrInsertBytes is unimplemented 856 func (s *Int16MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 857 panic("unimplemented") 858 } 859 860 type payloadUint16 struct { 861 val uint16 862 memoIdx int32 863 } 864 865 type entryUint16 struct { 866 h uint64 867 payload payloadUint16 868 } 869 870 func (e entryUint16) Valid() bool { return e.h != sentinel } 871 872 // Uint16HashTable is a hashtable specifically for uint16 that 873 // is utilized with the MemoTable to generalize interactions for easier 874 // implementation of dictionaries without losing performance. 875 type Uint16HashTable struct { 876 cap uint64 877 capMask uint64 878 size uint64 879 880 entries []entryUint16 881 } 882 883 // NewUint16HashTable returns a new hash table for uint16 values 884 // initialized with the passed in capacity or 32 whichever is larger. 885 func NewUint16HashTable(cap uint64) *Uint16HashTable { 886 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 887 ret := &Uint16HashTable{cap: initCap, capMask: initCap - 1, size: 0} 888 ret.entries = make([]entryUint16, initCap) 889 return ret 890 } 891 892 // Reset drops all of the values in this hash table and re-initializes it 893 // with the specified initial capacity as if by calling New, but without having 894 // to reallocate the object. 895 func (h *Uint16HashTable) Reset(cap uint64) { 896 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 897 h.capMask = h.cap - 1 898 h.size = 0 899 h.entries = make([]entryUint16, h.cap) 900 } 901 902 // CopyValues is used for copying the values out of the hash table into the 903 // passed in slice, in the order that they were first inserted 904 func (h *Uint16HashTable) CopyValues(out []uint16) { 905 h.CopyValuesSubset(0, out) 906 } 907 908 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 909 // with the value at start, in the order that they were inserted. 910 func (h *Uint16HashTable) CopyValuesSubset(start int, out []uint16) { 911 h.VisitEntries(func(e *entryUint16) { 912 idx := e.payload.memoIdx - int32(start) 913 if idx >= 0 { 914 out[idx] = e.payload.val 915 } 916 }) 917 } 918 919 func (h *Uint16HashTable) WriteOut(out []byte) { 920 h.WriteOutSubset(0, out) 921 } 922 923 func (h *Uint16HashTable) WriteOutSubset(start int, out []byte) { 924 data := arrow.Uint16Traits.CastFromBytes(out) 925 h.VisitEntries(func(e *entryUint16) { 926 idx := e.payload.memoIdx - int32(start) 927 if idx >= 0 { 928 data[idx] = utils.ToLEUint16(e.payload.val) 929 } 930 }) 931 } 932 933 func (h *Uint16HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 934 935 func (Uint16HashTable) fixHash(v uint64) uint64 { 936 if v == sentinel { 937 return 42 938 } 939 return v 940 } 941 942 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 943 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 944 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 945 func (h *Uint16HashTable) Lookup(v uint64, cmp func(uint16) bool) (*entryUint16, bool) { 946 idx, ok := h.lookup(v, h.capMask, cmp) 947 return &h.entries[idx], ok 948 } 949 950 func (h *Uint16HashTable) lookup(v uint64, szMask uint64, cmp func(uint16) bool) (uint64, bool) { 951 const perturbShift uint8 = 5 952 953 var ( 954 idx uint64 955 perturb uint64 956 e *entryUint16 957 ) 958 959 v = h.fixHash(v) 960 idx = v & szMask 961 perturb = (v >> uint64(perturbShift)) + 1 962 963 for { 964 e = &h.entries[idx] 965 if e.h == v && cmp(e.payload.val) { 966 return idx, true 967 } 968 969 if e.h == sentinel { 970 return idx, false 971 } 972 973 // perturbation logic inspired from CPython's set/dict object 974 // the goal is that all 64 bits of unmasked hash value eventually 975 // participate int he probing sequence, to minimize clustering 976 idx = (idx + perturb) & szMask 977 perturb = (perturb >> uint64(perturbShift)) + 1 978 } 979 } 980 981 func (h *Uint16HashTable) upsize(newcap uint64) error { 982 newMask := newcap - 1 983 984 oldEntries := h.entries 985 h.entries = make([]entryUint16, newcap) 986 for _, e := range oldEntries { 987 if e.Valid() { 988 idx, _ := h.lookup(e.h, newMask, func(uint16) bool { return false }) 989 h.entries[idx] = e 990 } 991 } 992 h.cap = newcap 993 h.capMask = newMask 994 return nil 995 } 996 997 // Insert updates the given entry with the provided hash value, payload value and memo index. 998 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 999 func (h *Uint16HashTable) Insert(e *entryUint16, v uint64, val uint16, memoIdx int32) error { 1000 e.h = h.fixHash(v) 1001 e.payload.val = val 1002 e.payload.memoIdx = memoIdx 1003 h.size++ 1004 1005 if h.needUpsize() { 1006 h.upsize(h.cap * uint64(loadFactor) * 2) 1007 } 1008 return nil 1009 } 1010 1011 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 1012 // a valid entry being one which has had a value inserted into it. 1013 func (h *Uint16HashTable) VisitEntries(visit func(*entryUint16)) { 1014 for _, e := range h.entries { 1015 if e.Valid() { 1016 visit(&e) 1017 } 1018 } 1019 } 1020 1021 // Uint16MemoTable is a wrapper over the appropriate hashtable to provide an interface 1022 // conforming to the MemoTable interface defined in the encoding package for general interactions 1023 // regarding dictionaries. 1024 type Uint16MemoTable struct { 1025 tbl *Uint16HashTable 1026 nullIdx int32 1027 } 1028 1029 // NewUint16MemoTable returns a new memotable with num entries pre-allocated to reduce further 1030 // allocations when inserting. 1031 func NewUint16MemoTable(num int64) *Uint16MemoTable { 1032 return &Uint16MemoTable{tbl: NewUint16HashTable(uint64(num)), nullIdx: KeyNotFound} 1033 } 1034 1035 func (Uint16MemoTable) TypeTraits() TypeTraits { 1036 return arrow.Uint16Traits 1037 } 1038 1039 // Reset allows this table to be re-used by dumping all the data currently in the table. 1040 func (s *Uint16MemoTable) Reset() { 1041 s.tbl.Reset(32) 1042 s.nullIdx = KeyNotFound 1043 } 1044 1045 // Size returns the current number of inserted elements into the table including if a null 1046 // has been inserted. 1047 func (s *Uint16MemoTable) Size() int { 1048 sz := int(s.tbl.size) 1049 if _, ok := s.GetNull(); ok { 1050 sz++ 1051 } 1052 return sz 1053 } 1054 1055 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 1056 // that will be true if found and false if not. 1057 func (s *Uint16MemoTable) GetNull() (int, bool) { 1058 return int(s.nullIdx), s.nullIdx != KeyNotFound 1059 } 1060 1061 // GetOrInsertNull will return the index of the null entry or insert a null entry 1062 // if one currently doesn't exist. The found value will be true if there was already 1063 // a null in the table, and false if it inserted one. 1064 func (s *Uint16MemoTable) GetOrInsertNull() (idx int, found bool) { 1065 idx, found = s.GetNull() 1066 if !found { 1067 idx = s.Size() 1068 s.nullIdx = int32(idx) 1069 } 1070 return 1071 } 1072 1073 // CopyValues will copy the values from the memo table out into the passed in slice 1074 // which must be of the appropriate type. 1075 func (s *Uint16MemoTable) CopyValues(out interface{}) { 1076 s.CopyValuesSubset(0, out) 1077 } 1078 1079 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 1080 // at the provided start index 1081 func (s *Uint16MemoTable) CopyValuesSubset(start int, out interface{}) { 1082 s.tbl.CopyValuesSubset(start, out.([]uint16)) 1083 } 1084 1085 func (s *Uint16MemoTable) WriteOut(out []byte) { 1086 s.tbl.CopyValues(arrow.Uint16Traits.CastFromBytes(out)) 1087 } 1088 1089 func (s *Uint16MemoTable) WriteOutSubset(start int, out []byte) { 1090 s.tbl.CopyValuesSubset(start, arrow.Uint16Traits.CastFromBytes(out)) 1091 } 1092 1093 func (s *Uint16MemoTable) WriteOutLE(out []byte) { 1094 s.tbl.WriteOut(out) 1095 } 1096 1097 func (s *Uint16MemoTable) WriteOutSubsetLE(start int, out []byte) { 1098 s.tbl.WriteOutSubset(start, out) 1099 } 1100 1101 // Get returns the index of the requested value in the hash table or KeyNotFound 1102 // along with a boolean indicating if it was found or not. 1103 func (s *Uint16MemoTable) Get(val interface{}) (int, bool) { 1104 1105 h := hashInt(uint64(val.(uint16)), 0) 1106 if e, ok := s.tbl.Lookup(h, func(v uint16) bool { return val.(uint16) == v }); ok { 1107 return int(e.payload.memoIdx), ok 1108 } 1109 return KeyNotFound, false 1110 } 1111 1112 // GetOrInsert will return the index of the specified value in the table, or insert the 1113 // value into the table and return the new index. found indicates whether or not it already 1114 // existed in the table (true) or was inserted by this call (false). 1115 func (s *Uint16MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 1116 1117 h := hashInt(uint64(val.(uint16)), 0) 1118 e, ok := s.tbl.Lookup(h, func(v uint16) bool { 1119 return val.(uint16) == v 1120 }) 1121 1122 if ok { 1123 idx = int(e.payload.memoIdx) 1124 found = true 1125 } else { 1126 idx = s.Size() 1127 s.tbl.Insert(e, h, val.(uint16), int32(idx)) 1128 } 1129 return 1130 } 1131 1132 // GetOrInsertBytes is unimplemented 1133 func (s *Uint16MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 1134 panic("unimplemented") 1135 } 1136 1137 type payloadInt32 struct { 1138 val int32 1139 memoIdx int32 1140 } 1141 1142 type entryInt32 struct { 1143 h uint64 1144 payload payloadInt32 1145 } 1146 1147 func (e entryInt32) Valid() bool { return e.h != sentinel } 1148 1149 // Int32HashTable is a hashtable specifically for int32 that 1150 // is utilized with the MemoTable to generalize interactions for easier 1151 // implementation of dictionaries without losing performance. 1152 type Int32HashTable struct { 1153 cap uint64 1154 capMask uint64 1155 size uint64 1156 1157 entries []entryInt32 1158 } 1159 1160 // NewInt32HashTable returns a new hash table for int32 values 1161 // initialized with the passed in capacity or 32 whichever is larger. 1162 func NewInt32HashTable(cap uint64) *Int32HashTable { 1163 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1164 ret := &Int32HashTable{cap: initCap, capMask: initCap - 1, size: 0} 1165 ret.entries = make([]entryInt32, initCap) 1166 return ret 1167 } 1168 1169 // Reset drops all of the values in this hash table and re-initializes it 1170 // with the specified initial capacity as if by calling New, but without having 1171 // to reallocate the object. 1172 func (h *Int32HashTable) Reset(cap uint64) { 1173 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1174 h.capMask = h.cap - 1 1175 h.size = 0 1176 h.entries = make([]entryInt32, h.cap) 1177 } 1178 1179 // CopyValues is used for copying the values out of the hash table into the 1180 // passed in slice, in the order that they were first inserted 1181 func (h *Int32HashTable) CopyValues(out []int32) { 1182 h.CopyValuesSubset(0, out) 1183 } 1184 1185 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 1186 // with the value at start, in the order that they were inserted. 1187 func (h *Int32HashTable) CopyValuesSubset(start int, out []int32) { 1188 h.VisitEntries(func(e *entryInt32) { 1189 idx := e.payload.memoIdx - int32(start) 1190 if idx >= 0 { 1191 out[idx] = e.payload.val 1192 } 1193 }) 1194 } 1195 1196 func (h *Int32HashTable) WriteOut(out []byte) { 1197 h.WriteOutSubset(0, out) 1198 } 1199 1200 func (h *Int32HashTable) WriteOutSubset(start int, out []byte) { 1201 data := arrow.Int32Traits.CastFromBytes(out) 1202 h.VisitEntries(func(e *entryInt32) { 1203 idx := e.payload.memoIdx - int32(start) 1204 if idx >= 0 { 1205 data[idx] = utils.ToLEInt32(e.payload.val) 1206 } 1207 }) 1208 } 1209 1210 func (h *Int32HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 1211 1212 func (Int32HashTable) fixHash(v uint64) uint64 { 1213 if v == sentinel { 1214 return 42 1215 } 1216 return v 1217 } 1218 1219 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 1220 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 1221 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 1222 func (h *Int32HashTable) Lookup(v uint64, cmp func(int32) bool) (*entryInt32, bool) { 1223 idx, ok := h.lookup(v, h.capMask, cmp) 1224 return &h.entries[idx], ok 1225 } 1226 1227 func (h *Int32HashTable) lookup(v uint64, szMask uint64, cmp func(int32) bool) (uint64, bool) { 1228 const perturbShift uint8 = 5 1229 1230 var ( 1231 idx uint64 1232 perturb uint64 1233 e *entryInt32 1234 ) 1235 1236 v = h.fixHash(v) 1237 idx = v & szMask 1238 perturb = (v >> uint64(perturbShift)) + 1 1239 1240 for { 1241 e = &h.entries[idx] 1242 if e.h == v && cmp(e.payload.val) { 1243 return idx, true 1244 } 1245 1246 if e.h == sentinel { 1247 return idx, false 1248 } 1249 1250 // perturbation logic inspired from CPython's set/dict object 1251 // the goal is that all 64 bits of unmasked hash value eventually 1252 // participate int he probing sequence, to minimize clustering 1253 idx = (idx + perturb) & szMask 1254 perturb = (perturb >> uint64(perturbShift)) + 1 1255 } 1256 } 1257 1258 func (h *Int32HashTable) upsize(newcap uint64) error { 1259 newMask := newcap - 1 1260 1261 oldEntries := h.entries 1262 h.entries = make([]entryInt32, newcap) 1263 for _, e := range oldEntries { 1264 if e.Valid() { 1265 idx, _ := h.lookup(e.h, newMask, func(int32) bool { return false }) 1266 h.entries[idx] = e 1267 } 1268 } 1269 h.cap = newcap 1270 h.capMask = newMask 1271 return nil 1272 } 1273 1274 // Insert updates the given entry with the provided hash value, payload value and memo index. 1275 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 1276 func (h *Int32HashTable) Insert(e *entryInt32, v uint64, val int32, memoIdx int32) error { 1277 e.h = h.fixHash(v) 1278 e.payload.val = val 1279 e.payload.memoIdx = memoIdx 1280 h.size++ 1281 1282 if h.needUpsize() { 1283 h.upsize(h.cap * uint64(loadFactor) * 2) 1284 } 1285 return nil 1286 } 1287 1288 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 1289 // a valid entry being one which has had a value inserted into it. 1290 func (h *Int32HashTable) VisitEntries(visit func(*entryInt32)) { 1291 for _, e := range h.entries { 1292 if e.Valid() { 1293 visit(&e) 1294 } 1295 } 1296 } 1297 1298 // Int32MemoTable is a wrapper over the appropriate hashtable to provide an interface 1299 // conforming to the MemoTable interface defined in the encoding package for general interactions 1300 // regarding dictionaries. 1301 type Int32MemoTable struct { 1302 tbl *Int32HashTable 1303 nullIdx int32 1304 } 1305 1306 // NewInt32MemoTable returns a new memotable with num entries pre-allocated to reduce further 1307 // allocations when inserting. 1308 func NewInt32MemoTable(num int64) *Int32MemoTable { 1309 return &Int32MemoTable{tbl: NewInt32HashTable(uint64(num)), nullIdx: KeyNotFound} 1310 } 1311 1312 func (Int32MemoTable) TypeTraits() TypeTraits { 1313 return arrow.Int32Traits 1314 } 1315 1316 // Reset allows this table to be re-used by dumping all the data currently in the table. 1317 func (s *Int32MemoTable) Reset() { 1318 s.tbl.Reset(32) 1319 s.nullIdx = KeyNotFound 1320 } 1321 1322 // Size returns the current number of inserted elements into the table including if a null 1323 // has been inserted. 1324 func (s *Int32MemoTable) Size() int { 1325 sz := int(s.tbl.size) 1326 if _, ok := s.GetNull(); ok { 1327 sz++ 1328 } 1329 return sz 1330 } 1331 1332 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 1333 // that will be true if found and false if not. 1334 func (s *Int32MemoTable) GetNull() (int, bool) { 1335 return int(s.nullIdx), s.nullIdx != KeyNotFound 1336 } 1337 1338 // GetOrInsertNull will return the index of the null entry or insert a null entry 1339 // if one currently doesn't exist. The found value will be true if there was already 1340 // a null in the table, and false if it inserted one. 1341 func (s *Int32MemoTable) GetOrInsertNull() (idx int, found bool) { 1342 idx, found = s.GetNull() 1343 if !found { 1344 idx = s.Size() 1345 s.nullIdx = int32(idx) 1346 } 1347 return 1348 } 1349 1350 // CopyValues will copy the values from the memo table out into the passed in slice 1351 // which must be of the appropriate type. 1352 func (s *Int32MemoTable) CopyValues(out interface{}) { 1353 s.CopyValuesSubset(0, out) 1354 } 1355 1356 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 1357 // at the provided start index 1358 func (s *Int32MemoTable) CopyValuesSubset(start int, out interface{}) { 1359 s.tbl.CopyValuesSubset(start, out.([]int32)) 1360 } 1361 1362 func (s *Int32MemoTable) WriteOut(out []byte) { 1363 s.tbl.CopyValues(arrow.Int32Traits.CastFromBytes(out)) 1364 } 1365 1366 func (s *Int32MemoTable) WriteOutSubset(start int, out []byte) { 1367 s.tbl.CopyValuesSubset(start, arrow.Int32Traits.CastFromBytes(out)) 1368 } 1369 1370 func (s *Int32MemoTable) WriteOutLE(out []byte) { 1371 s.tbl.WriteOut(out) 1372 } 1373 1374 func (s *Int32MemoTable) WriteOutSubsetLE(start int, out []byte) { 1375 s.tbl.WriteOutSubset(start, out) 1376 } 1377 1378 // Get returns the index of the requested value in the hash table or KeyNotFound 1379 // along with a boolean indicating if it was found or not. 1380 func (s *Int32MemoTable) Get(val interface{}) (int, bool) { 1381 1382 h := hashInt(uint64(val.(int32)), 0) 1383 if e, ok := s.tbl.Lookup(h, func(v int32) bool { return val.(int32) == v }); ok { 1384 return int(e.payload.memoIdx), ok 1385 } 1386 return KeyNotFound, false 1387 } 1388 1389 // GetOrInsert will return the index of the specified value in the table, or insert the 1390 // value into the table and return the new index. found indicates whether or not it already 1391 // existed in the table (true) or was inserted by this call (false). 1392 func (s *Int32MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 1393 1394 h := hashInt(uint64(val.(int32)), 0) 1395 e, ok := s.tbl.Lookup(h, func(v int32) bool { 1396 return val.(int32) == v 1397 }) 1398 1399 if ok { 1400 idx = int(e.payload.memoIdx) 1401 found = true 1402 } else { 1403 idx = s.Size() 1404 s.tbl.Insert(e, h, val.(int32), int32(idx)) 1405 } 1406 return 1407 } 1408 1409 // GetOrInsertBytes is unimplemented 1410 func (s *Int32MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 1411 panic("unimplemented") 1412 } 1413 1414 type payloadInt64 struct { 1415 val int64 1416 memoIdx int32 1417 } 1418 1419 type entryInt64 struct { 1420 h uint64 1421 payload payloadInt64 1422 } 1423 1424 func (e entryInt64) Valid() bool { return e.h != sentinel } 1425 1426 // Int64HashTable is a hashtable specifically for int64 that 1427 // is utilized with the MemoTable to generalize interactions for easier 1428 // implementation of dictionaries without losing performance. 1429 type Int64HashTable struct { 1430 cap uint64 1431 capMask uint64 1432 size uint64 1433 1434 entries []entryInt64 1435 } 1436 1437 // NewInt64HashTable returns a new hash table for int64 values 1438 // initialized with the passed in capacity or 32 whichever is larger. 1439 func NewInt64HashTable(cap uint64) *Int64HashTable { 1440 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1441 ret := &Int64HashTable{cap: initCap, capMask: initCap - 1, size: 0} 1442 ret.entries = make([]entryInt64, initCap) 1443 return ret 1444 } 1445 1446 // Reset drops all of the values in this hash table and re-initializes it 1447 // with the specified initial capacity as if by calling New, but without having 1448 // to reallocate the object. 1449 func (h *Int64HashTable) Reset(cap uint64) { 1450 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1451 h.capMask = h.cap - 1 1452 h.size = 0 1453 h.entries = make([]entryInt64, h.cap) 1454 } 1455 1456 // CopyValues is used for copying the values out of the hash table into the 1457 // passed in slice, in the order that they were first inserted 1458 func (h *Int64HashTable) CopyValues(out []int64) { 1459 h.CopyValuesSubset(0, out) 1460 } 1461 1462 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 1463 // with the value at start, in the order that they were inserted. 1464 func (h *Int64HashTable) CopyValuesSubset(start int, out []int64) { 1465 h.VisitEntries(func(e *entryInt64) { 1466 idx := e.payload.memoIdx - int32(start) 1467 if idx >= 0 { 1468 out[idx] = e.payload.val 1469 } 1470 }) 1471 } 1472 1473 func (h *Int64HashTable) WriteOut(out []byte) { 1474 h.WriteOutSubset(0, out) 1475 } 1476 1477 func (h *Int64HashTable) WriteOutSubset(start int, out []byte) { 1478 data := arrow.Int64Traits.CastFromBytes(out) 1479 h.VisitEntries(func(e *entryInt64) { 1480 idx := e.payload.memoIdx - int32(start) 1481 if idx >= 0 { 1482 data[idx] = utils.ToLEInt64(e.payload.val) 1483 } 1484 }) 1485 } 1486 1487 func (h *Int64HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 1488 1489 func (Int64HashTable) fixHash(v uint64) uint64 { 1490 if v == sentinel { 1491 return 42 1492 } 1493 return v 1494 } 1495 1496 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 1497 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 1498 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 1499 func (h *Int64HashTable) Lookup(v uint64, cmp func(int64) bool) (*entryInt64, bool) { 1500 idx, ok := h.lookup(v, h.capMask, cmp) 1501 return &h.entries[idx], ok 1502 } 1503 1504 func (h *Int64HashTable) lookup(v uint64, szMask uint64, cmp func(int64) bool) (uint64, bool) { 1505 const perturbShift uint8 = 5 1506 1507 var ( 1508 idx uint64 1509 perturb uint64 1510 e *entryInt64 1511 ) 1512 1513 v = h.fixHash(v) 1514 idx = v & szMask 1515 perturb = (v >> uint64(perturbShift)) + 1 1516 1517 for { 1518 e = &h.entries[idx] 1519 if e.h == v && cmp(e.payload.val) { 1520 return idx, true 1521 } 1522 1523 if e.h == sentinel { 1524 return idx, false 1525 } 1526 1527 // perturbation logic inspired from CPython's set/dict object 1528 // the goal is that all 64 bits of unmasked hash value eventually 1529 // participate int he probing sequence, to minimize clustering 1530 idx = (idx + perturb) & szMask 1531 perturb = (perturb >> uint64(perturbShift)) + 1 1532 } 1533 } 1534 1535 func (h *Int64HashTable) upsize(newcap uint64) error { 1536 newMask := newcap - 1 1537 1538 oldEntries := h.entries 1539 h.entries = make([]entryInt64, newcap) 1540 for _, e := range oldEntries { 1541 if e.Valid() { 1542 idx, _ := h.lookup(e.h, newMask, func(int64) bool { return false }) 1543 h.entries[idx] = e 1544 } 1545 } 1546 h.cap = newcap 1547 h.capMask = newMask 1548 return nil 1549 } 1550 1551 // Insert updates the given entry with the provided hash value, payload value and memo index. 1552 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 1553 func (h *Int64HashTable) Insert(e *entryInt64, v uint64, val int64, memoIdx int32) error { 1554 e.h = h.fixHash(v) 1555 e.payload.val = val 1556 e.payload.memoIdx = memoIdx 1557 h.size++ 1558 1559 if h.needUpsize() { 1560 h.upsize(h.cap * uint64(loadFactor) * 2) 1561 } 1562 return nil 1563 } 1564 1565 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 1566 // a valid entry being one which has had a value inserted into it. 1567 func (h *Int64HashTable) VisitEntries(visit func(*entryInt64)) { 1568 for _, e := range h.entries { 1569 if e.Valid() { 1570 visit(&e) 1571 } 1572 } 1573 } 1574 1575 // Int64MemoTable is a wrapper over the appropriate hashtable to provide an interface 1576 // conforming to the MemoTable interface defined in the encoding package for general interactions 1577 // regarding dictionaries. 1578 type Int64MemoTable struct { 1579 tbl *Int64HashTable 1580 nullIdx int32 1581 } 1582 1583 // NewInt64MemoTable returns a new memotable with num entries pre-allocated to reduce further 1584 // allocations when inserting. 1585 func NewInt64MemoTable(num int64) *Int64MemoTable { 1586 return &Int64MemoTable{tbl: NewInt64HashTable(uint64(num)), nullIdx: KeyNotFound} 1587 } 1588 1589 func (Int64MemoTable) TypeTraits() TypeTraits { 1590 return arrow.Int64Traits 1591 } 1592 1593 // Reset allows this table to be re-used by dumping all the data currently in the table. 1594 func (s *Int64MemoTable) Reset() { 1595 s.tbl.Reset(32) 1596 s.nullIdx = KeyNotFound 1597 } 1598 1599 // Size returns the current number of inserted elements into the table including if a null 1600 // has been inserted. 1601 func (s *Int64MemoTable) Size() int { 1602 sz := int(s.tbl.size) 1603 if _, ok := s.GetNull(); ok { 1604 sz++ 1605 } 1606 return sz 1607 } 1608 1609 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 1610 // that will be true if found and false if not. 1611 func (s *Int64MemoTable) GetNull() (int, bool) { 1612 return int(s.nullIdx), s.nullIdx != KeyNotFound 1613 } 1614 1615 // GetOrInsertNull will return the index of the null entry or insert a null entry 1616 // if one currently doesn't exist. The found value will be true if there was already 1617 // a null in the table, and false if it inserted one. 1618 func (s *Int64MemoTable) GetOrInsertNull() (idx int, found bool) { 1619 idx, found = s.GetNull() 1620 if !found { 1621 idx = s.Size() 1622 s.nullIdx = int32(idx) 1623 } 1624 return 1625 } 1626 1627 // CopyValues will copy the values from the memo table out into the passed in slice 1628 // which must be of the appropriate type. 1629 func (s *Int64MemoTable) CopyValues(out interface{}) { 1630 s.CopyValuesSubset(0, out) 1631 } 1632 1633 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 1634 // at the provided start index 1635 func (s *Int64MemoTable) CopyValuesSubset(start int, out interface{}) { 1636 s.tbl.CopyValuesSubset(start, out.([]int64)) 1637 } 1638 1639 func (s *Int64MemoTable) WriteOut(out []byte) { 1640 s.tbl.CopyValues(arrow.Int64Traits.CastFromBytes(out)) 1641 } 1642 1643 func (s *Int64MemoTable) WriteOutSubset(start int, out []byte) { 1644 s.tbl.CopyValuesSubset(start, arrow.Int64Traits.CastFromBytes(out)) 1645 } 1646 1647 func (s *Int64MemoTable) WriteOutLE(out []byte) { 1648 s.tbl.WriteOut(out) 1649 } 1650 1651 func (s *Int64MemoTable) WriteOutSubsetLE(start int, out []byte) { 1652 s.tbl.WriteOutSubset(start, out) 1653 } 1654 1655 // Get returns the index of the requested value in the hash table or KeyNotFound 1656 // along with a boolean indicating if it was found or not. 1657 func (s *Int64MemoTable) Get(val interface{}) (int, bool) { 1658 1659 h := hashInt(uint64(val.(int64)), 0) 1660 if e, ok := s.tbl.Lookup(h, func(v int64) bool { return val.(int64) == v }); ok { 1661 return int(e.payload.memoIdx), ok 1662 } 1663 return KeyNotFound, false 1664 } 1665 1666 // GetOrInsert will return the index of the specified value in the table, or insert the 1667 // value into the table and return the new index. found indicates whether or not it already 1668 // existed in the table (true) or was inserted by this call (false). 1669 func (s *Int64MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 1670 1671 h := hashInt(uint64(val.(int64)), 0) 1672 e, ok := s.tbl.Lookup(h, func(v int64) bool { 1673 return val.(int64) == v 1674 }) 1675 1676 if ok { 1677 idx = int(e.payload.memoIdx) 1678 found = true 1679 } else { 1680 idx = s.Size() 1681 s.tbl.Insert(e, h, val.(int64), int32(idx)) 1682 } 1683 return 1684 } 1685 1686 // GetOrInsertBytes is unimplemented 1687 func (s *Int64MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 1688 panic("unimplemented") 1689 } 1690 1691 type payloadUint32 struct { 1692 val uint32 1693 memoIdx int32 1694 } 1695 1696 type entryUint32 struct { 1697 h uint64 1698 payload payloadUint32 1699 } 1700 1701 func (e entryUint32) Valid() bool { return e.h != sentinel } 1702 1703 // Uint32HashTable is a hashtable specifically for uint32 that 1704 // is utilized with the MemoTable to generalize interactions for easier 1705 // implementation of dictionaries without losing performance. 1706 type Uint32HashTable struct { 1707 cap uint64 1708 capMask uint64 1709 size uint64 1710 1711 entries []entryUint32 1712 } 1713 1714 // NewUint32HashTable returns a new hash table for uint32 values 1715 // initialized with the passed in capacity or 32 whichever is larger. 1716 func NewUint32HashTable(cap uint64) *Uint32HashTable { 1717 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1718 ret := &Uint32HashTable{cap: initCap, capMask: initCap - 1, size: 0} 1719 ret.entries = make([]entryUint32, initCap) 1720 return ret 1721 } 1722 1723 // Reset drops all of the values in this hash table and re-initializes it 1724 // with the specified initial capacity as if by calling New, but without having 1725 // to reallocate the object. 1726 func (h *Uint32HashTable) Reset(cap uint64) { 1727 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1728 h.capMask = h.cap - 1 1729 h.size = 0 1730 h.entries = make([]entryUint32, h.cap) 1731 } 1732 1733 // CopyValues is used for copying the values out of the hash table into the 1734 // passed in slice, in the order that they were first inserted 1735 func (h *Uint32HashTable) CopyValues(out []uint32) { 1736 h.CopyValuesSubset(0, out) 1737 } 1738 1739 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 1740 // with the value at start, in the order that they were inserted. 1741 func (h *Uint32HashTable) CopyValuesSubset(start int, out []uint32) { 1742 h.VisitEntries(func(e *entryUint32) { 1743 idx := e.payload.memoIdx - int32(start) 1744 if idx >= 0 { 1745 out[idx] = e.payload.val 1746 } 1747 }) 1748 } 1749 1750 func (h *Uint32HashTable) WriteOut(out []byte) { 1751 h.WriteOutSubset(0, out) 1752 } 1753 1754 func (h *Uint32HashTable) WriteOutSubset(start int, out []byte) { 1755 data := arrow.Uint32Traits.CastFromBytes(out) 1756 h.VisitEntries(func(e *entryUint32) { 1757 idx := e.payload.memoIdx - int32(start) 1758 if idx >= 0 { 1759 data[idx] = utils.ToLEUint32(e.payload.val) 1760 } 1761 }) 1762 } 1763 1764 func (h *Uint32HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 1765 1766 func (Uint32HashTable) fixHash(v uint64) uint64 { 1767 if v == sentinel { 1768 return 42 1769 } 1770 return v 1771 } 1772 1773 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 1774 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 1775 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 1776 func (h *Uint32HashTable) Lookup(v uint64, cmp func(uint32) bool) (*entryUint32, bool) { 1777 idx, ok := h.lookup(v, h.capMask, cmp) 1778 return &h.entries[idx], ok 1779 } 1780 1781 func (h *Uint32HashTable) lookup(v uint64, szMask uint64, cmp func(uint32) bool) (uint64, bool) { 1782 const perturbShift uint8 = 5 1783 1784 var ( 1785 idx uint64 1786 perturb uint64 1787 e *entryUint32 1788 ) 1789 1790 v = h.fixHash(v) 1791 idx = v & szMask 1792 perturb = (v >> uint64(perturbShift)) + 1 1793 1794 for { 1795 e = &h.entries[idx] 1796 if e.h == v && cmp(e.payload.val) { 1797 return idx, true 1798 } 1799 1800 if e.h == sentinel { 1801 return idx, false 1802 } 1803 1804 // perturbation logic inspired from CPython's set/dict object 1805 // the goal is that all 64 bits of unmasked hash value eventually 1806 // participate int he probing sequence, to minimize clustering 1807 idx = (idx + perturb) & szMask 1808 perturb = (perturb >> uint64(perturbShift)) + 1 1809 } 1810 } 1811 1812 func (h *Uint32HashTable) upsize(newcap uint64) error { 1813 newMask := newcap - 1 1814 1815 oldEntries := h.entries 1816 h.entries = make([]entryUint32, newcap) 1817 for _, e := range oldEntries { 1818 if e.Valid() { 1819 idx, _ := h.lookup(e.h, newMask, func(uint32) bool { return false }) 1820 h.entries[idx] = e 1821 } 1822 } 1823 h.cap = newcap 1824 h.capMask = newMask 1825 return nil 1826 } 1827 1828 // Insert updates the given entry with the provided hash value, payload value and memo index. 1829 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 1830 func (h *Uint32HashTable) Insert(e *entryUint32, v uint64, val uint32, memoIdx int32) error { 1831 e.h = h.fixHash(v) 1832 e.payload.val = val 1833 e.payload.memoIdx = memoIdx 1834 h.size++ 1835 1836 if h.needUpsize() { 1837 h.upsize(h.cap * uint64(loadFactor) * 2) 1838 } 1839 return nil 1840 } 1841 1842 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 1843 // a valid entry being one which has had a value inserted into it. 1844 func (h *Uint32HashTable) VisitEntries(visit func(*entryUint32)) { 1845 for _, e := range h.entries { 1846 if e.Valid() { 1847 visit(&e) 1848 } 1849 } 1850 } 1851 1852 // Uint32MemoTable is a wrapper over the appropriate hashtable to provide an interface 1853 // conforming to the MemoTable interface defined in the encoding package for general interactions 1854 // regarding dictionaries. 1855 type Uint32MemoTable struct { 1856 tbl *Uint32HashTable 1857 nullIdx int32 1858 } 1859 1860 // NewUint32MemoTable returns a new memotable with num entries pre-allocated to reduce further 1861 // allocations when inserting. 1862 func NewUint32MemoTable(num int64) *Uint32MemoTable { 1863 return &Uint32MemoTable{tbl: NewUint32HashTable(uint64(num)), nullIdx: KeyNotFound} 1864 } 1865 1866 func (Uint32MemoTable) TypeTraits() TypeTraits { 1867 return arrow.Uint32Traits 1868 } 1869 1870 // Reset allows this table to be re-used by dumping all the data currently in the table. 1871 func (s *Uint32MemoTable) Reset() { 1872 s.tbl.Reset(32) 1873 s.nullIdx = KeyNotFound 1874 } 1875 1876 // Size returns the current number of inserted elements into the table including if a null 1877 // has been inserted. 1878 func (s *Uint32MemoTable) Size() int { 1879 sz := int(s.tbl.size) 1880 if _, ok := s.GetNull(); ok { 1881 sz++ 1882 } 1883 return sz 1884 } 1885 1886 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 1887 // that will be true if found and false if not. 1888 func (s *Uint32MemoTable) GetNull() (int, bool) { 1889 return int(s.nullIdx), s.nullIdx != KeyNotFound 1890 } 1891 1892 // GetOrInsertNull will return the index of the null entry or insert a null entry 1893 // if one currently doesn't exist. The found value will be true if there was already 1894 // a null in the table, and false if it inserted one. 1895 func (s *Uint32MemoTable) GetOrInsertNull() (idx int, found bool) { 1896 idx, found = s.GetNull() 1897 if !found { 1898 idx = s.Size() 1899 s.nullIdx = int32(idx) 1900 } 1901 return 1902 } 1903 1904 // CopyValues will copy the values from the memo table out into the passed in slice 1905 // which must be of the appropriate type. 1906 func (s *Uint32MemoTable) CopyValues(out interface{}) { 1907 s.CopyValuesSubset(0, out) 1908 } 1909 1910 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 1911 // at the provided start index 1912 func (s *Uint32MemoTable) CopyValuesSubset(start int, out interface{}) { 1913 s.tbl.CopyValuesSubset(start, out.([]uint32)) 1914 } 1915 1916 func (s *Uint32MemoTable) WriteOut(out []byte) { 1917 s.tbl.CopyValues(arrow.Uint32Traits.CastFromBytes(out)) 1918 } 1919 1920 func (s *Uint32MemoTable) WriteOutSubset(start int, out []byte) { 1921 s.tbl.CopyValuesSubset(start, arrow.Uint32Traits.CastFromBytes(out)) 1922 } 1923 1924 func (s *Uint32MemoTable) WriteOutLE(out []byte) { 1925 s.tbl.WriteOut(out) 1926 } 1927 1928 func (s *Uint32MemoTable) WriteOutSubsetLE(start int, out []byte) { 1929 s.tbl.WriteOutSubset(start, out) 1930 } 1931 1932 // Get returns the index of the requested value in the hash table or KeyNotFound 1933 // along with a boolean indicating if it was found or not. 1934 func (s *Uint32MemoTable) Get(val interface{}) (int, bool) { 1935 1936 h := hashInt(uint64(val.(uint32)), 0) 1937 if e, ok := s.tbl.Lookup(h, func(v uint32) bool { return val.(uint32) == v }); ok { 1938 return int(e.payload.memoIdx), ok 1939 } 1940 return KeyNotFound, false 1941 } 1942 1943 // GetOrInsert will return the index of the specified value in the table, or insert the 1944 // value into the table and return the new index. found indicates whether or not it already 1945 // existed in the table (true) or was inserted by this call (false). 1946 func (s *Uint32MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 1947 1948 h := hashInt(uint64(val.(uint32)), 0) 1949 e, ok := s.tbl.Lookup(h, func(v uint32) bool { 1950 return val.(uint32) == v 1951 }) 1952 1953 if ok { 1954 idx = int(e.payload.memoIdx) 1955 found = true 1956 } else { 1957 idx = s.Size() 1958 s.tbl.Insert(e, h, val.(uint32), int32(idx)) 1959 } 1960 return 1961 } 1962 1963 // GetOrInsertBytes is unimplemented 1964 func (s *Uint32MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 1965 panic("unimplemented") 1966 } 1967 1968 type payloadUint64 struct { 1969 val uint64 1970 memoIdx int32 1971 } 1972 1973 type entryUint64 struct { 1974 h uint64 1975 payload payloadUint64 1976 } 1977 1978 func (e entryUint64) Valid() bool { return e.h != sentinel } 1979 1980 // Uint64HashTable is a hashtable specifically for uint64 that 1981 // is utilized with the MemoTable to generalize interactions for easier 1982 // implementation of dictionaries without losing performance. 1983 type Uint64HashTable struct { 1984 cap uint64 1985 capMask uint64 1986 size uint64 1987 1988 entries []entryUint64 1989 } 1990 1991 // NewUint64HashTable returns a new hash table for uint64 values 1992 // initialized with the passed in capacity or 32 whichever is larger. 1993 func NewUint64HashTable(cap uint64) *Uint64HashTable { 1994 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 1995 ret := &Uint64HashTable{cap: initCap, capMask: initCap - 1, size: 0} 1996 ret.entries = make([]entryUint64, initCap) 1997 return ret 1998 } 1999 2000 // Reset drops all of the values in this hash table and re-initializes it 2001 // with the specified initial capacity as if by calling New, but without having 2002 // to reallocate the object. 2003 func (h *Uint64HashTable) Reset(cap uint64) { 2004 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 2005 h.capMask = h.cap - 1 2006 h.size = 0 2007 h.entries = make([]entryUint64, h.cap) 2008 } 2009 2010 // CopyValues is used for copying the values out of the hash table into the 2011 // passed in slice, in the order that they were first inserted 2012 func (h *Uint64HashTable) CopyValues(out []uint64) { 2013 h.CopyValuesSubset(0, out) 2014 } 2015 2016 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 2017 // with the value at start, in the order that they were inserted. 2018 func (h *Uint64HashTable) CopyValuesSubset(start int, out []uint64) { 2019 h.VisitEntries(func(e *entryUint64) { 2020 idx := e.payload.memoIdx - int32(start) 2021 if idx >= 0 { 2022 out[idx] = e.payload.val 2023 } 2024 }) 2025 } 2026 2027 func (h *Uint64HashTable) WriteOut(out []byte) { 2028 h.WriteOutSubset(0, out) 2029 } 2030 2031 func (h *Uint64HashTable) WriteOutSubset(start int, out []byte) { 2032 data := arrow.Uint64Traits.CastFromBytes(out) 2033 h.VisitEntries(func(e *entryUint64) { 2034 idx := e.payload.memoIdx - int32(start) 2035 if idx >= 0 { 2036 data[idx] = utils.ToLEUint64(e.payload.val) 2037 } 2038 }) 2039 } 2040 2041 func (h *Uint64HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 2042 2043 func (Uint64HashTable) fixHash(v uint64) uint64 { 2044 if v == sentinel { 2045 return 42 2046 } 2047 return v 2048 } 2049 2050 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 2051 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 2052 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 2053 func (h *Uint64HashTable) Lookup(v uint64, cmp func(uint64) bool) (*entryUint64, bool) { 2054 idx, ok := h.lookup(v, h.capMask, cmp) 2055 return &h.entries[idx], ok 2056 } 2057 2058 func (h *Uint64HashTable) lookup(v uint64, szMask uint64, cmp func(uint64) bool) (uint64, bool) { 2059 const perturbShift uint8 = 5 2060 2061 var ( 2062 idx uint64 2063 perturb uint64 2064 e *entryUint64 2065 ) 2066 2067 v = h.fixHash(v) 2068 idx = v & szMask 2069 perturb = (v >> uint64(perturbShift)) + 1 2070 2071 for { 2072 e = &h.entries[idx] 2073 if e.h == v && cmp(e.payload.val) { 2074 return idx, true 2075 } 2076 2077 if e.h == sentinel { 2078 return idx, false 2079 } 2080 2081 // perturbation logic inspired from CPython's set/dict object 2082 // the goal is that all 64 bits of unmasked hash value eventually 2083 // participate int he probing sequence, to minimize clustering 2084 idx = (idx + perturb) & szMask 2085 perturb = (perturb >> uint64(perturbShift)) + 1 2086 } 2087 } 2088 2089 func (h *Uint64HashTable) upsize(newcap uint64) error { 2090 newMask := newcap - 1 2091 2092 oldEntries := h.entries 2093 h.entries = make([]entryUint64, newcap) 2094 for _, e := range oldEntries { 2095 if e.Valid() { 2096 idx, _ := h.lookup(e.h, newMask, func(uint64) bool { return false }) 2097 h.entries[idx] = e 2098 } 2099 } 2100 h.cap = newcap 2101 h.capMask = newMask 2102 return nil 2103 } 2104 2105 // Insert updates the given entry with the provided hash value, payload value and memo index. 2106 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 2107 func (h *Uint64HashTable) Insert(e *entryUint64, v uint64, val uint64, memoIdx int32) error { 2108 e.h = h.fixHash(v) 2109 e.payload.val = val 2110 e.payload.memoIdx = memoIdx 2111 h.size++ 2112 2113 if h.needUpsize() { 2114 h.upsize(h.cap * uint64(loadFactor) * 2) 2115 } 2116 return nil 2117 } 2118 2119 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 2120 // a valid entry being one which has had a value inserted into it. 2121 func (h *Uint64HashTable) VisitEntries(visit func(*entryUint64)) { 2122 for _, e := range h.entries { 2123 if e.Valid() { 2124 visit(&e) 2125 } 2126 } 2127 } 2128 2129 // Uint64MemoTable is a wrapper over the appropriate hashtable to provide an interface 2130 // conforming to the MemoTable interface defined in the encoding package for general interactions 2131 // regarding dictionaries. 2132 type Uint64MemoTable struct { 2133 tbl *Uint64HashTable 2134 nullIdx int32 2135 } 2136 2137 // NewUint64MemoTable returns a new memotable with num entries pre-allocated to reduce further 2138 // allocations when inserting. 2139 func NewUint64MemoTable(num int64) *Uint64MemoTable { 2140 return &Uint64MemoTable{tbl: NewUint64HashTable(uint64(num)), nullIdx: KeyNotFound} 2141 } 2142 2143 func (Uint64MemoTable) TypeTraits() TypeTraits { 2144 return arrow.Uint64Traits 2145 } 2146 2147 // Reset allows this table to be re-used by dumping all the data currently in the table. 2148 func (s *Uint64MemoTable) Reset() { 2149 s.tbl.Reset(32) 2150 s.nullIdx = KeyNotFound 2151 } 2152 2153 // Size returns the current number of inserted elements into the table including if a null 2154 // has been inserted. 2155 func (s *Uint64MemoTable) Size() int { 2156 sz := int(s.tbl.size) 2157 if _, ok := s.GetNull(); ok { 2158 sz++ 2159 } 2160 return sz 2161 } 2162 2163 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 2164 // that will be true if found and false if not. 2165 func (s *Uint64MemoTable) GetNull() (int, bool) { 2166 return int(s.nullIdx), s.nullIdx != KeyNotFound 2167 } 2168 2169 // GetOrInsertNull will return the index of the null entry or insert a null entry 2170 // if one currently doesn't exist. The found value will be true if there was already 2171 // a null in the table, and false if it inserted one. 2172 func (s *Uint64MemoTable) GetOrInsertNull() (idx int, found bool) { 2173 idx, found = s.GetNull() 2174 if !found { 2175 idx = s.Size() 2176 s.nullIdx = int32(idx) 2177 } 2178 return 2179 } 2180 2181 // CopyValues will copy the values from the memo table out into the passed in slice 2182 // which must be of the appropriate type. 2183 func (s *Uint64MemoTable) CopyValues(out interface{}) { 2184 s.CopyValuesSubset(0, out) 2185 } 2186 2187 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 2188 // at the provided start index 2189 func (s *Uint64MemoTable) CopyValuesSubset(start int, out interface{}) { 2190 s.tbl.CopyValuesSubset(start, out.([]uint64)) 2191 } 2192 2193 func (s *Uint64MemoTable) WriteOut(out []byte) { 2194 s.tbl.CopyValues(arrow.Uint64Traits.CastFromBytes(out)) 2195 } 2196 2197 func (s *Uint64MemoTable) WriteOutSubset(start int, out []byte) { 2198 s.tbl.CopyValuesSubset(start, arrow.Uint64Traits.CastFromBytes(out)) 2199 } 2200 2201 func (s *Uint64MemoTable) WriteOutLE(out []byte) { 2202 s.tbl.WriteOut(out) 2203 } 2204 2205 func (s *Uint64MemoTable) WriteOutSubsetLE(start int, out []byte) { 2206 s.tbl.WriteOutSubset(start, out) 2207 } 2208 2209 // Get returns the index of the requested value in the hash table or KeyNotFound 2210 // along with a boolean indicating if it was found or not. 2211 func (s *Uint64MemoTable) Get(val interface{}) (int, bool) { 2212 2213 h := hashInt(uint64(val.(uint64)), 0) 2214 if e, ok := s.tbl.Lookup(h, func(v uint64) bool { return val.(uint64) == v }); ok { 2215 return int(e.payload.memoIdx), ok 2216 } 2217 return KeyNotFound, false 2218 } 2219 2220 // GetOrInsert will return the index of the specified value in the table, or insert the 2221 // value into the table and return the new index. found indicates whether or not it already 2222 // existed in the table (true) or was inserted by this call (false). 2223 func (s *Uint64MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 2224 2225 h := hashInt(uint64(val.(uint64)), 0) 2226 e, ok := s.tbl.Lookup(h, func(v uint64) bool { 2227 return val.(uint64) == v 2228 }) 2229 2230 if ok { 2231 idx = int(e.payload.memoIdx) 2232 found = true 2233 } else { 2234 idx = s.Size() 2235 s.tbl.Insert(e, h, val.(uint64), int32(idx)) 2236 } 2237 return 2238 } 2239 2240 // GetOrInsertBytes is unimplemented 2241 func (s *Uint64MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 2242 panic("unimplemented") 2243 } 2244 2245 type payloadFloat32 struct { 2246 val float32 2247 memoIdx int32 2248 } 2249 2250 type entryFloat32 struct { 2251 h uint64 2252 payload payloadFloat32 2253 } 2254 2255 func (e entryFloat32) Valid() bool { return e.h != sentinel } 2256 2257 // Float32HashTable is a hashtable specifically for float32 that 2258 // is utilized with the MemoTable to generalize interactions for easier 2259 // implementation of dictionaries without losing performance. 2260 type Float32HashTable struct { 2261 cap uint64 2262 capMask uint64 2263 size uint64 2264 2265 entries []entryFloat32 2266 } 2267 2268 // NewFloat32HashTable returns a new hash table for float32 values 2269 // initialized with the passed in capacity or 32 whichever is larger. 2270 func NewFloat32HashTable(cap uint64) *Float32HashTable { 2271 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 2272 ret := &Float32HashTable{cap: initCap, capMask: initCap - 1, size: 0} 2273 ret.entries = make([]entryFloat32, initCap) 2274 return ret 2275 } 2276 2277 // Reset drops all of the values in this hash table and re-initializes it 2278 // with the specified initial capacity as if by calling New, but without having 2279 // to reallocate the object. 2280 func (h *Float32HashTable) Reset(cap uint64) { 2281 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 2282 h.capMask = h.cap - 1 2283 h.size = 0 2284 h.entries = make([]entryFloat32, h.cap) 2285 } 2286 2287 // CopyValues is used for copying the values out of the hash table into the 2288 // passed in slice, in the order that they were first inserted 2289 func (h *Float32HashTable) CopyValues(out []float32) { 2290 h.CopyValuesSubset(0, out) 2291 } 2292 2293 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 2294 // with the value at start, in the order that they were inserted. 2295 func (h *Float32HashTable) CopyValuesSubset(start int, out []float32) { 2296 h.VisitEntries(func(e *entryFloat32) { 2297 idx := e.payload.memoIdx - int32(start) 2298 if idx >= 0 { 2299 out[idx] = e.payload.val 2300 } 2301 }) 2302 } 2303 2304 func (h *Float32HashTable) WriteOut(out []byte) { 2305 h.WriteOutSubset(0, out) 2306 } 2307 2308 func (h *Float32HashTable) WriteOutSubset(start int, out []byte) { 2309 data := arrow.Float32Traits.CastFromBytes(out) 2310 h.VisitEntries(func(e *entryFloat32) { 2311 idx := e.payload.memoIdx - int32(start) 2312 if idx >= 0 { 2313 data[idx] = utils.ToLEFloat32(e.payload.val) 2314 } 2315 }) 2316 } 2317 2318 func (h *Float32HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 2319 2320 func (Float32HashTable) fixHash(v uint64) uint64 { 2321 if v == sentinel { 2322 return 42 2323 } 2324 return v 2325 } 2326 2327 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 2328 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 2329 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 2330 func (h *Float32HashTable) Lookup(v uint64, cmp func(float32) bool) (*entryFloat32, bool) { 2331 idx, ok := h.lookup(v, h.capMask, cmp) 2332 return &h.entries[idx], ok 2333 } 2334 2335 func (h *Float32HashTable) lookup(v uint64, szMask uint64, cmp func(float32) bool) (uint64, bool) { 2336 const perturbShift uint8 = 5 2337 2338 var ( 2339 idx uint64 2340 perturb uint64 2341 e *entryFloat32 2342 ) 2343 2344 v = h.fixHash(v) 2345 idx = v & szMask 2346 perturb = (v >> uint64(perturbShift)) + 1 2347 2348 for { 2349 e = &h.entries[idx] 2350 if e.h == v && cmp(e.payload.val) { 2351 return idx, true 2352 } 2353 2354 if e.h == sentinel { 2355 return idx, false 2356 } 2357 2358 // perturbation logic inspired from CPython's set/dict object 2359 // the goal is that all 64 bits of unmasked hash value eventually 2360 // participate int he probing sequence, to minimize clustering 2361 idx = (idx + perturb) & szMask 2362 perturb = (perturb >> uint64(perturbShift)) + 1 2363 } 2364 } 2365 2366 func (h *Float32HashTable) upsize(newcap uint64) error { 2367 newMask := newcap - 1 2368 2369 oldEntries := h.entries 2370 h.entries = make([]entryFloat32, newcap) 2371 for _, e := range oldEntries { 2372 if e.Valid() { 2373 idx, _ := h.lookup(e.h, newMask, func(float32) bool { return false }) 2374 h.entries[idx] = e 2375 } 2376 } 2377 h.cap = newcap 2378 h.capMask = newMask 2379 return nil 2380 } 2381 2382 // Insert updates the given entry with the provided hash value, payload value and memo index. 2383 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 2384 func (h *Float32HashTable) Insert(e *entryFloat32, v uint64, val float32, memoIdx int32) error { 2385 e.h = h.fixHash(v) 2386 e.payload.val = val 2387 e.payload.memoIdx = memoIdx 2388 h.size++ 2389 2390 if h.needUpsize() { 2391 h.upsize(h.cap * uint64(loadFactor) * 2) 2392 } 2393 return nil 2394 } 2395 2396 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 2397 // a valid entry being one which has had a value inserted into it. 2398 func (h *Float32HashTable) VisitEntries(visit func(*entryFloat32)) { 2399 for _, e := range h.entries { 2400 if e.Valid() { 2401 visit(&e) 2402 } 2403 } 2404 } 2405 2406 // Float32MemoTable is a wrapper over the appropriate hashtable to provide an interface 2407 // conforming to the MemoTable interface defined in the encoding package for general interactions 2408 // regarding dictionaries. 2409 type Float32MemoTable struct { 2410 tbl *Float32HashTable 2411 nullIdx int32 2412 } 2413 2414 // NewFloat32MemoTable returns a new memotable with num entries pre-allocated to reduce further 2415 // allocations when inserting. 2416 func NewFloat32MemoTable(num int64) *Float32MemoTable { 2417 return &Float32MemoTable{tbl: NewFloat32HashTable(uint64(num)), nullIdx: KeyNotFound} 2418 } 2419 2420 func (Float32MemoTable) TypeTraits() TypeTraits { 2421 return arrow.Float32Traits 2422 } 2423 2424 // Reset allows this table to be re-used by dumping all the data currently in the table. 2425 func (s *Float32MemoTable) Reset() { 2426 s.tbl.Reset(32) 2427 s.nullIdx = KeyNotFound 2428 } 2429 2430 // Size returns the current number of inserted elements into the table including if a null 2431 // has been inserted. 2432 func (s *Float32MemoTable) Size() int { 2433 sz := int(s.tbl.size) 2434 if _, ok := s.GetNull(); ok { 2435 sz++ 2436 } 2437 return sz 2438 } 2439 2440 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 2441 // that will be true if found and false if not. 2442 func (s *Float32MemoTable) GetNull() (int, bool) { 2443 return int(s.nullIdx), s.nullIdx != KeyNotFound 2444 } 2445 2446 // GetOrInsertNull will return the index of the null entry or insert a null entry 2447 // if one currently doesn't exist. The found value will be true if there was already 2448 // a null in the table, and false if it inserted one. 2449 func (s *Float32MemoTable) GetOrInsertNull() (idx int, found bool) { 2450 idx, found = s.GetNull() 2451 if !found { 2452 idx = s.Size() 2453 s.nullIdx = int32(idx) 2454 } 2455 return 2456 } 2457 2458 // CopyValues will copy the values from the memo table out into the passed in slice 2459 // which must be of the appropriate type. 2460 func (s *Float32MemoTable) CopyValues(out interface{}) { 2461 s.CopyValuesSubset(0, out) 2462 } 2463 2464 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 2465 // at the provided start index 2466 func (s *Float32MemoTable) CopyValuesSubset(start int, out interface{}) { 2467 s.tbl.CopyValuesSubset(start, out.([]float32)) 2468 } 2469 2470 func (s *Float32MemoTable) WriteOut(out []byte) { 2471 s.tbl.CopyValues(arrow.Float32Traits.CastFromBytes(out)) 2472 } 2473 2474 func (s *Float32MemoTable) WriteOutSubset(start int, out []byte) { 2475 s.tbl.CopyValuesSubset(start, arrow.Float32Traits.CastFromBytes(out)) 2476 } 2477 2478 func (s *Float32MemoTable) WriteOutLE(out []byte) { 2479 s.tbl.WriteOut(out) 2480 } 2481 2482 func (s *Float32MemoTable) WriteOutSubsetLE(start int, out []byte) { 2483 s.tbl.WriteOutSubset(start, out) 2484 } 2485 2486 // Get returns the index of the requested value in the hash table or KeyNotFound 2487 // along with a boolean indicating if it was found or not. 2488 func (s *Float32MemoTable) Get(val interface{}) (int, bool) { 2489 var cmp func(float32) bool 2490 2491 if math.IsNaN(float64(val.(float32))) { 2492 cmp = isNan32Cmp 2493 // use consistent internal bit pattern for NaN regardless of the pattern 2494 // that is passed to us. NaN is NaN is NaN 2495 val = float32(math.NaN()) 2496 } else { 2497 cmp = func(v float32) bool { return val.(float32) == v } 2498 } 2499 2500 h := hashFloat32(val.(float32), 0) 2501 if e, ok := s.tbl.Lookup(h, cmp); ok { 2502 return int(e.payload.memoIdx), ok 2503 } 2504 return KeyNotFound, false 2505 } 2506 2507 // GetOrInsert will return the index of the specified value in the table, or insert the 2508 // value into the table and return the new index. found indicates whether or not it already 2509 // existed in the table (true) or was inserted by this call (false). 2510 func (s *Float32MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 2511 2512 var cmp func(float32) bool 2513 2514 if math.IsNaN(float64(val.(float32))) { 2515 cmp = isNan32Cmp 2516 // use consistent internal bit pattern for NaN regardless of the pattern 2517 // that is passed to us. NaN is NaN is NaN 2518 val = float32(math.NaN()) 2519 } else { 2520 cmp = func(v float32) bool { return val.(float32) == v } 2521 } 2522 2523 h := hashFloat32(val.(float32), 0) 2524 e, ok := s.tbl.Lookup(h, cmp) 2525 2526 if ok { 2527 idx = int(e.payload.memoIdx) 2528 found = true 2529 } else { 2530 idx = s.Size() 2531 s.tbl.Insert(e, h, val.(float32), int32(idx)) 2532 } 2533 return 2534 } 2535 2536 // GetOrInsertBytes is unimplemented 2537 func (s *Float32MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 2538 panic("unimplemented") 2539 } 2540 2541 type payloadFloat64 struct { 2542 val float64 2543 memoIdx int32 2544 } 2545 2546 type entryFloat64 struct { 2547 h uint64 2548 payload payloadFloat64 2549 } 2550 2551 func (e entryFloat64) Valid() bool { return e.h != sentinel } 2552 2553 // Float64HashTable is a hashtable specifically for float64 that 2554 // is utilized with the MemoTable to generalize interactions for easier 2555 // implementation of dictionaries without losing performance. 2556 type Float64HashTable struct { 2557 cap uint64 2558 capMask uint64 2559 size uint64 2560 2561 entries []entryFloat64 2562 } 2563 2564 // NewFloat64HashTable returns a new hash table for float64 values 2565 // initialized with the passed in capacity or 32 whichever is larger. 2566 func NewFloat64HashTable(cap uint64) *Float64HashTable { 2567 initCap := uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 2568 ret := &Float64HashTable{cap: initCap, capMask: initCap - 1, size: 0} 2569 ret.entries = make([]entryFloat64, initCap) 2570 return ret 2571 } 2572 2573 // Reset drops all of the values in this hash table and re-initializes it 2574 // with the specified initial capacity as if by calling New, but without having 2575 // to reallocate the object. 2576 func (h *Float64HashTable) Reset(cap uint64) { 2577 h.cap = uint64(bitutil.NextPowerOf2(int(max(cap, 32)))) 2578 h.capMask = h.cap - 1 2579 h.size = 0 2580 h.entries = make([]entryFloat64, h.cap) 2581 } 2582 2583 // CopyValues is used for copying the values out of the hash table into the 2584 // passed in slice, in the order that they were first inserted 2585 func (h *Float64HashTable) CopyValues(out []float64) { 2586 h.CopyValuesSubset(0, out) 2587 } 2588 2589 // CopyValuesSubset copies a subset of the values in the hashtable out, starting 2590 // with the value at start, in the order that they were inserted. 2591 func (h *Float64HashTable) CopyValuesSubset(start int, out []float64) { 2592 h.VisitEntries(func(e *entryFloat64) { 2593 idx := e.payload.memoIdx - int32(start) 2594 if idx >= 0 { 2595 out[idx] = e.payload.val 2596 } 2597 }) 2598 } 2599 2600 func (h *Float64HashTable) WriteOut(out []byte) { 2601 h.WriteOutSubset(0, out) 2602 } 2603 2604 func (h *Float64HashTable) WriteOutSubset(start int, out []byte) { 2605 data := arrow.Float64Traits.CastFromBytes(out) 2606 h.VisitEntries(func(e *entryFloat64) { 2607 idx := e.payload.memoIdx - int32(start) 2608 if idx >= 0 { 2609 data[idx] = utils.ToLEFloat64(e.payload.val) 2610 } 2611 }) 2612 } 2613 2614 func (h *Float64HashTable) needUpsize() bool { return h.size*uint64(loadFactor) >= h.cap } 2615 2616 func (Float64HashTable) fixHash(v uint64) uint64 { 2617 if v == sentinel { 2618 return 42 2619 } 2620 return v 2621 } 2622 2623 // Lookup retrieves the entry for a given hash value assuming it's payload value returns 2624 // true when passed to the cmp func. Returns a pointer to the entry for the given hash value, 2625 // and a boolean as to whether it was found. It is not safe to use the pointer if the bool is false. 2626 func (h *Float64HashTable) Lookup(v uint64, cmp func(float64) bool) (*entryFloat64, bool) { 2627 idx, ok := h.lookup(v, h.capMask, cmp) 2628 return &h.entries[idx], ok 2629 } 2630 2631 func (h *Float64HashTable) lookup(v uint64, szMask uint64, cmp func(float64) bool) (uint64, bool) { 2632 const perturbShift uint8 = 5 2633 2634 var ( 2635 idx uint64 2636 perturb uint64 2637 e *entryFloat64 2638 ) 2639 2640 v = h.fixHash(v) 2641 idx = v & szMask 2642 perturb = (v >> uint64(perturbShift)) + 1 2643 2644 for { 2645 e = &h.entries[idx] 2646 if e.h == v && cmp(e.payload.val) { 2647 return idx, true 2648 } 2649 2650 if e.h == sentinel { 2651 return idx, false 2652 } 2653 2654 // perturbation logic inspired from CPython's set/dict object 2655 // the goal is that all 64 bits of unmasked hash value eventually 2656 // participate int he probing sequence, to minimize clustering 2657 idx = (idx + perturb) & szMask 2658 perturb = (perturb >> uint64(perturbShift)) + 1 2659 } 2660 } 2661 2662 func (h *Float64HashTable) upsize(newcap uint64) error { 2663 newMask := newcap - 1 2664 2665 oldEntries := h.entries 2666 h.entries = make([]entryFloat64, newcap) 2667 for _, e := range oldEntries { 2668 if e.Valid() { 2669 idx, _ := h.lookup(e.h, newMask, func(float64) bool { return false }) 2670 h.entries[idx] = e 2671 } 2672 } 2673 h.cap = newcap 2674 h.capMask = newMask 2675 return nil 2676 } 2677 2678 // Insert updates the given entry with the provided hash value, payload value and memo index. 2679 // The entry pointer must have been retrieved via lookup in order to actually insert properly. 2680 func (h *Float64HashTable) Insert(e *entryFloat64, v uint64, val float64, memoIdx int32) error { 2681 e.h = h.fixHash(v) 2682 e.payload.val = val 2683 e.payload.memoIdx = memoIdx 2684 h.size++ 2685 2686 if h.needUpsize() { 2687 h.upsize(h.cap * uint64(loadFactor) * 2) 2688 } 2689 return nil 2690 } 2691 2692 // VisitEntries will call the passed in function on each *valid* entry in the hash table, 2693 // a valid entry being one which has had a value inserted into it. 2694 func (h *Float64HashTable) VisitEntries(visit func(*entryFloat64)) { 2695 for _, e := range h.entries { 2696 if e.Valid() { 2697 visit(&e) 2698 } 2699 } 2700 } 2701 2702 // Float64MemoTable is a wrapper over the appropriate hashtable to provide an interface 2703 // conforming to the MemoTable interface defined in the encoding package for general interactions 2704 // regarding dictionaries. 2705 type Float64MemoTable struct { 2706 tbl *Float64HashTable 2707 nullIdx int32 2708 } 2709 2710 // NewFloat64MemoTable returns a new memotable with num entries pre-allocated to reduce further 2711 // allocations when inserting. 2712 func NewFloat64MemoTable(num int64) *Float64MemoTable { 2713 return &Float64MemoTable{tbl: NewFloat64HashTable(uint64(num)), nullIdx: KeyNotFound} 2714 } 2715 2716 func (Float64MemoTable) TypeTraits() TypeTraits { 2717 return arrow.Float64Traits 2718 } 2719 2720 // Reset allows this table to be re-used by dumping all the data currently in the table. 2721 func (s *Float64MemoTable) Reset() { 2722 s.tbl.Reset(32) 2723 s.nullIdx = KeyNotFound 2724 } 2725 2726 // Size returns the current number of inserted elements into the table including if a null 2727 // has been inserted. 2728 func (s *Float64MemoTable) Size() int { 2729 sz := int(s.tbl.size) 2730 if _, ok := s.GetNull(); ok { 2731 sz++ 2732 } 2733 return sz 2734 } 2735 2736 // GetNull returns the index of an inserted null or KeyNotFound along with a bool 2737 // that will be true if found and false if not. 2738 func (s *Float64MemoTable) GetNull() (int, bool) { 2739 return int(s.nullIdx), s.nullIdx != KeyNotFound 2740 } 2741 2742 // GetOrInsertNull will return the index of the null entry or insert a null entry 2743 // if one currently doesn't exist. The found value will be true if there was already 2744 // a null in the table, and false if it inserted one. 2745 func (s *Float64MemoTable) GetOrInsertNull() (idx int, found bool) { 2746 idx, found = s.GetNull() 2747 if !found { 2748 idx = s.Size() 2749 s.nullIdx = int32(idx) 2750 } 2751 return 2752 } 2753 2754 // CopyValues will copy the values from the memo table out into the passed in slice 2755 // which must be of the appropriate type. 2756 func (s *Float64MemoTable) CopyValues(out interface{}) { 2757 s.CopyValuesSubset(0, out) 2758 } 2759 2760 // CopyValuesSubset is like CopyValues but only copies a subset of values starting 2761 // at the provided start index 2762 func (s *Float64MemoTable) CopyValuesSubset(start int, out interface{}) { 2763 s.tbl.CopyValuesSubset(start, out.([]float64)) 2764 } 2765 2766 func (s *Float64MemoTable) WriteOut(out []byte) { 2767 s.tbl.CopyValues(arrow.Float64Traits.CastFromBytes(out)) 2768 } 2769 2770 func (s *Float64MemoTable) WriteOutSubset(start int, out []byte) { 2771 s.tbl.CopyValuesSubset(start, arrow.Float64Traits.CastFromBytes(out)) 2772 } 2773 2774 func (s *Float64MemoTable) WriteOutLE(out []byte) { 2775 s.tbl.WriteOut(out) 2776 } 2777 2778 func (s *Float64MemoTable) WriteOutSubsetLE(start int, out []byte) { 2779 s.tbl.WriteOutSubset(start, out) 2780 } 2781 2782 // Get returns the index of the requested value in the hash table or KeyNotFound 2783 // along with a boolean indicating if it was found or not. 2784 func (s *Float64MemoTable) Get(val interface{}) (int, bool) { 2785 var cmp func(float64) bool 2786 if math.IsNaN(val.(float64)) { 2787 cmp = math.IsNaN 2788 // use consistent internal bit pattern for NaN regardless of the pattern 2789 // that is passed to us. NaN is NaN is NaN 2790 val = math.NaN() 2791 } else { 2792 cmp = func(v float64) bool { return val.(float64) == v } 2793 } 2794 2795 h := hashFloat64(val.(float64), 0) 2796 if e, ok := s.tbl.Lookup(h, cmp); ok { 2797 return int(e.payload.memoIdx), ok 2798 } 2799 return KeyNotFound, false 2800 } 2801 2802 // GetOrInsert will return the index of the specified value in the table, or insert the 2803 // value into the table and return the new index. found indicates whether or not it already 2804 // existed in the table (true) or was inserted by this call (false). 2805 func (s *Float64MemoTable) GetOrInsert(val interface{}) (idx int, found bool, err error) { 2806 2807 var cmp func(float64) bool 2808 if math.IsNaN(val.(float64)) { 2809 cmp = math.IsNaN 2810 // use consistent internal bit pattern for NaN regardless of the pattern 2811 // that is passed to us. NaN is NaN is NaN 2812 val = math.NaN() 2813 } else { 2814 cmp = func(v float64) bool { return val.(float64) == v } 2815 } 2816 2817 h := hashFloat64(val.(float64), 0) 2818 e, ok := s.tbl.Lookup(h, cmp) 2819 2820 if ok { 2821 idx = int(e.payload.memoIdx) 2822 found = true 2823 } else { 2824 idx = s.Size() 2825 s.tbl.Insert(e, h, val.(float64), int32(idx)) 2826 } 2827 return 2828 } 2829 2830 // GetOrInsertBytes is unimplemented 2831 func (s *Float64MemoTable) GetOrInsertBytes(val []byte) (idx int, found bool, err error) { 2832 panic("unimplemented") 2833 }