123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742 |
- package huff0
- import (
- "fmt"
- "math"
- "runtime"
- "sync"
- )
- // Compress1X will compress the input.
- // The output can be decoded using Decompress1X.
- // Supply a Scratch object. The scratch object contains state about re-use,
- // So when sharing across independent encodes, be sure to set the re-use policy.
- func Compress1X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) {
- s, err = s.prepare(in)
- if err != nil {
- return nil, false, err
- }
- return compress(in, s, s.compress1X)
- }
- // Compress4X will compress the input. The input is split into 4 independent blocks
- // and compressed similar to Compress1X.
- // The output can be decoded using Decompress4X.
- // Supply a Scratch object. The scratch object contains state about re-use,
- // So when sharing across independent encodes, be sure to set the re-use policy.
- func Compress4X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) {
- s, err = s.prepare(in)
- if err != nil {
- return nil, false, err
- }
- if false {
- // TODO: compress4Xp only slightly faster.
- const parallelThreshold = 8 << 10
- if len(in) < parallelThreshold || runtime.GOMAXPROCS(0) == 1 {
- return compress(in, s, s.compress4X)
- }
- return compress(in, s, s.compress4Xp)
- }
- return compress(in, s, s.compress4X)
- }
- func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)) (out []byte, reUsed bool, err error) {
- // Nuke previous table if we cannot reuse anyway.
- if s.Reuse == ReusePolicyNone {
- s.prevTable = s.prevTable[:0]
- }
- // Create histogram, if none was provided.
- maxCount := s.maxCount
- var canReuse = false
- if maxCount == 0 {
- maxCount, canReuse = s.countSimple(in)
- } else {
- canReuse = s.canUseTable(s.prevTable)
- }
- // We want the output size to be less than this:
- wantSize := len(in)
- if s.WantLogLess > 0 {
- wantSize -= wantSize >> s.WantLogLess
- }
- // Reset for next run.
- s.clearCount = true
- s.maxCount = 0
- if maxCount >= len(in) {
- if maxCount > len(in) {
- return nil, false, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in))
- }
- if len(in) == 1 {
- return nil, false, ErrIncompressible
- }
- // One symbol, use RLE
- return nil, false, ErrUseRLE
- }
- if maxCount == 1 || maxCount < (len(in)>>7) {
- // Each symbol present maximum once or too well distributed.
- return nil, false, ErrIncompressible
- }
- if s.Reuse == ReusePolicyMust && !canReuse {
- // We must reuse, but we can't.
- return nil, false, ErrIncompressible
- }
- if (s.Reuse == ReusePolicyPrefer || s.Reuse == ReusePolicyMust) && canReuse {
- keepTable := s.cTable
- keepTL := s.actualTableLog
- s.cTable = s.prevTable
- s.actualTableLog = s.prevTableLog
- s.Out, err = compressor(in)
- s.cTable = keepTable
- s.actualTableLog = keepTL
- if err == nil && len(s.Out) < wantSize {
- s.OutData = s.Out
- return s.Out, true, nil
- }
- if s.Reuse == ReusePolicyMust {
- return nil, false, ErrIncompressible
- }
- // Do not attempt to re-use later.
- s.prevTable = s.prevTable[:0]
- }
- // Calculate new table.
- err = s.buildCTable()
- if err != nil {
- return nil, false, err
- }
- if false && !s.canUseTable(s.cTable) {
- panic("invalid table generated")
- }
- if s.Reuse == ReusePolicyAllow && canReuse {
- hSize := len(s.Out)
- oldSize := s.prevTable.estimateSize(s.count[:s.symbolLen])
- newSize := s.cTable.estimateSize(s.count[:s.symbolLen])
- if oldSize <= hSize+newSize || hSize+12 >= wantSize {
- // Retain cTable even if we re-use.
- keepTable := s.cTable
- keepTL := s.actualTableLog
- s.cTable = s.prevTable
- s.actualTableLog = s.prevTableLog
- s.Out, err = compressor(in)
- // Restore ctable.
- s.cTable = keepTable
- s.actualTableLog = keepTL
- if err != nil {
- return nil, false, err
- }
- if len(s.Out) >= wantSize {
- return nil, false, ErrIncompressible
- }
- s.OutData = s.Out
- return s.Out, true, nil
- }
- }
- // Use new table
- err = s.cTable.write(s)
- if err != nil {
- s.OutTable = nil
- return nil, false, err
- }
- s.OutTable = s.Out
- // Compress using new table
- s.Out, err = compressor(in)
- if err != nil {
- s.OutTable = nil
- return nil, false, err
- }
- if len(s.Out) >= wantSize {
- s.OutTable = nil
- return nil, false, ErrIncompressible
- }
- // Move current table into previous.
- s.prevTable, s.prevTableLog, s.cTable = s.cTable, s.actualTableLog, s.prevTable[:0]
- s.OutData = s.Out[len(s.OutTable):]
- return s.Out, false, nil
- }
- // EstimateSizes will estimate the data sizes
- func EstimateSizes(in []byte, s *Scratch) (tableSz, dataSz, reuseSz int, err error) {
- s, err = s.prepare(in)
- if err != nil {
- return 0, 0, 0, err
- }
- // Create histogram, if none was provided.
- tableSz, dataSz, reuseSz = -1, -1, -1
- maxCount := s.maxCount
- var canReuse = false
- if maxCount == 0 {
- maxCount, canReuse = s.countSimple(in)
- } else {
- canReuse = s.canUseTable(s.prevTable)
- }
- // We want the output size to be less than this:
- wantSize := len(in)
- if s.WantLogLess > 0 {
- wantSize -= wantSize >> s.WantLogLess
- }
- // Reset for next run.
- s.clearCount = true
- s.maxCount = 0
- if maxCount >= len(in) {
- if maxCount > len(in) {
- return 0, 0, 0, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in))
- }
- if len(in) == 1 {
- return 0, 0, 0, ErrIncompressible
- }
- // One symbol, use RLE
- return 0, 0, 0, ErrUseRLE
- }
- if maxCount == 1 || maxCount < (len(in)>>7) {
- // Each symbol present maximum once or too well distributed.
- return 0, 0, 0, ErrIncompressible
- }
- // Calculate new table.
- err = s.buildCTable()
- if err != nil {
- return 0, 0, 0, err
- }
- if false && !s.canUseTable(s.cTable) {
- panic("invalid table generated")
- }
- tableSz, err = s.cTable.estTableSize(s)
- if err != nil {
- return 0, 0, 0, err
- }
- if canReuse {
- reuseSz = s.prevTable.estimateSize(s.count[:s.symbolLen])
- }
- dataSz = s.cTable.estimateSize(s.count[:s.symbolLen])
- // Restore
- return tableSz, dataSz, reuseSz, nil
- }
- func (s *Scratch) compress1X(src []byte) ([]byte, error) {
- return s.compress1xDo(s.Out, src), nil
- }
- func (s *Scratch) compress1xDo(dst, src []byte) []byte {
- var bw = bitWriter{out: dst}
- // N is length divisible by 4.
- n := len(src)
- n -= n & 3
- cTable := s.cTable[:256]
- // Encode last bytes.
- for i := len(src) & 3; i > 0; i-- {
- bw.encSymbol(cTable, src[n+i-1])
- }
- n -= 4
- if s.actualTableLog <= 8 {
- for ; n >= 0; n -= 4 {
- tmp := src[n : n+4]
- // tmp should be len 4
- bw.flush32()
- bw.encFourSymbols(cTable[tmp[3]], cTable[tmp[2]], cTable[tmp[1]], cTable[tmp[0]])
- }
- } else {
- for ; n >= 0; n -= 4 {
- tmp := src[n : n+4]
- // tmp should be len 4
- bw.flush32()
- bw.encTwoSymbols(cTable, tmp[3], tmp[2])
- bw.flush32()
- bw.encTwoSymbols(cTable, tmp[1], tmp[0])
- }
- }
- bw.close()
- return bw.out
- }
- var sixZeros [6]byte
- func (s *Scratch) compress4X(src []byte) ([]byte, error) {
- if len(src) < 12 {
- return nil, ErrIncompressible
- }
- segmentSize := (len(src) + 3) / 4
- // Add placeholder for output length
- offsetIdx := len(s.Out)
- s.Out = append(s.Out, sixZeros[:]...)
- for i := 0; i < 4; i++ {
- toDo := src
- if len(toDo) > segmentSize {
- toDo = toDo[:segmentSize]
- }
- src = src[len(toDo):]
- idx := len(s.Out)
- s.Out = s.compress1xDo(s.Out, toDo)
- if len(s.Out)-idx > math.MaxUint16 {
- // We cannot store the size in the jump table
- return nil, ErrIncompressible
- }
- // Write compressed length as little endian before block.
- if i < 3 {
- // Last length is not written.
- length := len(s.Out) - idx
- s.Out[i*2+offsetIdx] = byte(length)
- s.Out[i*2+offsetIdx+1] = byte(length >> 8)
- }
- }
- return s.Out, nil
- }
- // compress4Xp will compress 4 streams using separate goroutines.
- func (s *Scratch) compress4Xp(src []byte) ([]byte, error) {
- if len(src) < 12 {
- return nil, ErrIncompressible
- }
- // Add placeholder for output length
- s.Out = s.Out[:6]
- segmentSize := (len(src) + 3) / 4
- var wg sync.WaitGroup
- wg.Add(4)
- for i := 0; i < 4; i++ {
- toDo := src
- if len(toDo) > segmentSize {
- toDo = toDo[:segmentSize]
- }
- src = src[len(toDo):]
- // Separate goroutine for each block.
- go func(i int) {
- s.tmpOut[i] = s.compress1xDo(s.tmpOut[i][:0], toDo)
- wg.Done()
- }(i)
- }
- wg.Wait()
- for i := 0; i < 4; i++ {
- o := s.tmpOut[i]
- if len(o) > math.MaxUint16 {
- // We cannot store the size in the jump table
- return nil, ErrIncompressible
- }
- // Write compressed length as little endian before block.
- if i < 3 {
- // Last length is not written.
- s.Out[i*2] = byte(len(o))
- s.Out[i*2+1] = byte(len(o) >> 8)
- }
- // Write output.
- s.Out = append(s.Out, o...)
- }
- return s.Out, nil
- }
- // countSimple will create a simple histogram in s.count.
- // Returns the biggest count.
- // Does not update s.clearCount.
- func (s *Scratch) countSimple(in []byte) (max int, reuse bool) {
- reuse = true
- _ = s.count // Assert that s != nil to speed up the following loop.
- for _, v := range in {
- s.count[v]++
- }
- m := uint32(0)
- if len(s.prevTable) > 0 {
- for i, v := range s.count[:] {
- if v == 0 {
- continue
- }
- if v > m {
- m = v
- }
- s.symbolLen = uint16(i) + 1
- if i >= len(s.prevTable) {
- reuse = false
- } else if s.prevTable[i].nBits == 0 {
- reuse = false
- }
- }
- return int(m), reuse
- }
- for i, v := range s.count[:] {
- if v == 0 {
- continue
- }
- if v > m {
- m = v
- }
- s.symbolLen = uint16(i) + 1
- }
- return int(m), false
- }
- func (s *Scratch) canUseTable(c cTable) bool {
- if len(c) < int(s.symbolLen) {
- return false
- }
- for i, v := range s.count[:s.symbolLen] {
- if v != 0 && c[i].nBits == 0 {
- return false
- }
- }
- return true
- }
- //lint:ignore U1000 used for debugging
- func (s *Scratch) validateTable(c cTable) bool {
- if len(c) < int(s.symbolLen) {
- return false
- }
- for i, v := range s.count[:s.symbolLen] {
- if v != 0 {
- if c[i].nBits == 0 {
- return false
- }
- if c[i].nBits > s.actualTableLog {
- return false
- }
- }
- }
- return true
- }
- // minTableLog provides the minimum logSize to safely represent a distribution.
- func (s *Scratch) minTableLog() uint8 {
- minBitsSrc := highBit32(uint32(s.srcLen)) + 1
- minBitsSymbols := highBit32(uint32(s.symbolLen-1)) + 2
- if minBitsSrc < minBitsSymbols {
- return uint8(minBitsSrc)
- }
- return uint8(minBitsSymbols)
- }
- // optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog
- func (s *Scratch) optimalTableLog() {
- tableLog := s.TableLog
- minBits := s.minTableLog()
- maxBitsSrc := uint8(highBit32(uint32(s.srcLen-1))) - 1
- if maxBitsSrc < tableLog {
- // Accuracy can be reduced
- tableLog = maxBitsSrc
- }
- if minBits > tableLog {
- tableLog = minBits
- }
- // Need a minimum to safely represent all symbol values
- if tableLog < minTablelog {
- tableLog = minTablelog
- }
- if tableLog > tableLogMax {
- tableLog = tableLogMax
- }
- s.actualTableLog = tableLog
- }
- type cTableEntry struct {
- val uint16
- nBits uint8
- // We have 8 bits extra
- }
- const huffNodesMask = huffNodesLen - 1
- func (s *Scratch) buildCTable() error {
- s.optimalTableLog()
- s.huffSort()
- if cap(s.cTable) < maxSymbolValue+1 {
- s.cTable = make([]cTableEntry, s.symbolLen, maxSymbolValue+1)
- } else {
- s.cTable = s.cTable[:s.symbolLen]
- for i := range s.cTable {
- s.cTable[i] = cTableEntry{}
- }
- }
- var startNode = int16(s.symbolLen)
- nonNullRank := s.symbolLen - 1
- nodeNb := startNode
- huffNode := s.nodes[1 : huffNodesLen+1]
- // This overlays the slice above, but allows "-1" index lookups.
- // Different from reference implementation.
- huffNode0 := s.nodes[0 : huffNodesLen+1]
- for huffNode[nonNullRank].count() == 0 {
- nonNullRank--
- }
- lowS := int16(nonNullRank)
- nodeRoot := nodeNb + lowS - 1
- lowN := nodeNb
- huffNode[nodeNb].setCount(huffNode[lowS].count() + huffNode[lowS-1].count())
- huffNode[lowS].setParent(nodeNb)
- huffNode[lowS-1].setParent(nodeNb)
- nodeNb++
- lowS -= 2
- for n := nodeNb; n <= nodeRoot; n++ {
- huffNode[n].setCount(1 << 30)
- }
- // fake entry, strong barrier
- huffNode0[0].setCount(1 << 31)
- // create parents
- for nodeNb <= nodeRoot {
- var n1, n2 int16
- if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() {
- n1 = lowS
- lowS--
- } else {
- n1 = lowN
- lowN++
- }
- if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() {
- n2 = lowS
- lowS--
- } else {
- n2 = lowN
- lowN++
- }
- huffNode[nodeNb].setCount(huffNode0[n1+1].count() + huffNode0[n2+1].count())
- huffNode0[n1+1].setParent(nodeNb)
- huffNode0[n2+1].setParent(nodeNb)
- nodeNb++
- }
- // distribute weights (unlimited tree height)
- huffNode[nodeRoot].setNbBits(0)
- for n := nodeRoot - 1; n >= startNode; n-- {
- huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1)
- }
- for n := uint16(0); n <= nonNullRank; n++ {
- huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1)
- }
- s.actualTableLog = s.setMaxHeight(int(nonNullRank))
- maxNbBits := s.actualTableLog
- // fill result into tree (val, nbBits)
- if maxNbBits > tableLogMax {
- return fmt.Errorf("internal error: maxNbBits (%d) > tableLogMax (%d)", maxNbBits, tableLogMax)
- }
- var nbPerRank [tableLogMax + 1]uint16
- var valPerRank [16]uint16
- for _, v := range huffNode[:nonNullRank+1] {
- nbPerRank[v.nbBits()]++
- }
- // determine stating value per rank
- {
- min := uint16(0)
- for n := maxNbBits; n > 0; n-- {
- // get starting value within each rank
- valPerRank[n] = min
- min += nbPerRank[n]
- min >>= 1
- }
- }
- // push nbBits per symbol, symbol order
- for _, v := range huffNode[:nonNullRank+1] {
- s.cTable[v.symbol()].nBits = v.nbBits()
- }
- // assign value within rank, symbol order
- t := s.cTable[:s.symbolLen]
- for n, val := range t {
- nbits := val.nBits & 15
- v := valPerRank[nbits]
- t[n].val = v
- valPerRank[nbits] = v + 1
- }
- return nil
- }
- // huffSort will sort symbols, decreasing order.
- func (s *Scratch) huffSort() {
- type rankPos struct {
- base uint32
- current uint32
- }
- // Clear nodes
- nodes := s.nodes[:huffNodesLen+1]
- s.nodes = nodes
- nodes = nodes[1 : huffNodesLen+1]
- // Sort into buckets based on length of symbol count.
- var rank [32]rankPos
- for _, v := range s.count[:s.symbolLen] {
- r := highBit32(v+1) & 31
- rank[r].base++
- }
- // maxBitLength is log2(BlockSizeMax) + 1
- const maxBitLength = 18 + 1
- for n := maxBitLength; n > 0; n-- {
- rank[n-1].base += rank[n].base
- }
- for n := range rank[:maxBitLength] {
- rank[n].current = rank[n].base
- }
- for n, c := range s.count[:s.symbolLen] {
- r := (highBit32(c+1) + 1) & 31
- pos := rank[r].current
- rank[r].current++
- prev := nodes[(pos-1)&huffNodesMask]
- for pos > rank[r].base && c > prev.count() {
- nodes[pos&huffNodesMask] = prev
- pos--
- prev = nodes[(pos-1)&huffNodesMask]
- }
- nodes[pos&huffNodesMask] = makeNodeElt(c, byte(n))
- }
- }
- func (s *Scratch) setMaxHeight(lastNonNull int) uint8 {
- maxNbBits := s.actualTableLog
- huffNode := s.nodes[1 : huffNodesLen+1]
- //huffNode = huffNode[: huffNodesLen]
- largestBits := huffNode[lastNonNull].nbBits()
- // early exit : no elt > maxNbBits
- if largestBits <= maxNbBits {
- return largestBits
- }
- totalCost := int(0)
- baseCost := int(1) << (largestBits - maxNbBits)
- n := uint32(lastNonNull)
- for huffNode[n].nbBits() > maxNbBits {
- totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits()))
- huffNode[n].setNbBits(maxNbBits)
- n--
- }
- // n stops at huffNode[n].nbBits <= maxNbBits
- for huffNode[n].nbBits() == maxNbBits {
- n--
- }
- // n end at index of smallest symbol using < maxNbBits
- // renorm totalCost
- totalCost >>= largestBits - maxNbBits /* note : totalCost is necessarily a multiple of baseCost */
- // repay normalized cost
- {
- const noSymbol = 0xF0F0F0F0
- var rankLast [tableLogMax + 2]uint32
- for i := range rankLast[:] {
- rankLast[i] = noSymbol
- }
- // Get pos of last (smallest) symbol per rank
- {
- currentNbBits := maxNbBits
- for pos := int(n); pos >= 0; pos-- {
- if huffNode[pos].nbBits() >= currentNbBits {
- continue
- }
- currentNbBits = huffNode[pos].nbBits() // < maxNbBits
- rankLast[maxNbBits-currentNbBits] = uint32(pos)
- }
- }
- for totalCost > 0 {
- nBitsToDecrease := uint8(highBit32(uint32(totalCost))) + 1
- for ; nBitsToDecrease > 1; nBitsToDecrease-- {
- highPos := rankLast[nBitsToDecrease]
- lowPos := rankLast[nBitsToDecrease-1]
- if highPos == noSymbol {
- continue
- }
- if lowPos == noSymbol {
- break
- }
- highTotal := huffNode[highPos].count()
- lowTotal := 2 * huffNode[lowPos].count()
- if highTotal <= lowTotal {
- break
- }
- }
- // only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !)
- // HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary
- // FIXME: try to remove
- for (nBitsToDecrease <= tableLogMax) && (rankLast[nBitsToDecrease] == noSymbol) {
- nBitsToDecrease++
- }
- totalCost -= 1 << (nBitsToDecrease - 1)
- if rankLast[nBitsToDecrease-1] == noSymbol {
- // this rank is no longer empty
- rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease]
- }
- huffNode[rankLast[nBitsToDecrease]].setNbBits(1 +
- huffNode[rankLast[nBitsToDecrease]].nbBits())
- if rankLast[nBitsToDecrease] == 0 {
- /* special case, reached largest symbol */
- rankLast[nBitsToDecrease] = noSymbol
- } else {
- rankLast[nBitsToDecrease]--
- if huffNode[rankLast[nBitsToDecrease]].nbBits() != maxNbBits-nBitsToDecrease {
- rankLast[nBitsToDecrease] = noSymbol /* this rank is now empty */
- }
- }
- }
- for totalCost < 0 { /* Sometimes, cost correction overshoot */
- if rankLast[1] == noSymbol { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */
- for huffNode[n].nbBits() == maxNbBits {
- n--
- }
- huffNode[n+1].setNbBits(huffNode[n+1].nbBits() - 1)
- rankLast[1] = n + 1
- totalCost++
- continue
- }
- huffNode[rankLast[1]+1].setNbBits(huffNode[rankLast[1]+1].nbBits() - 1)
- rankLast[1]++
- totalCost++
- }
- }
- return maxNbBits
- }
- // A nodeElt is the fields
- //
- // count uint32
- // parent uint16
- // symbol byte
- // nbBits uint8
- //
- // in some order, all squashed into an integer so that the compiler
- // always loads and stores entire nodeElts instead of separate fields.
- type nodeElt uint64
- func makeNodeElt(count uint32, symbol byte) nodeElt {
- return nodeElt(count) | nodeElt(symbol)<<48
- }
- func (e *nodeElt) count() uint32 { return uint32(*e) }
- func (e *nodeElt) parent() uint16 { return uint16(*e >> 32) }
- func (e *nodeElt) symbol() byte { return byte(*e >> 48) }
- func (e *nodeElt) nbBits() uint8 { return uint8(*e >> 56) }
- func (e *nodeElt) setCount(c uint32) { *e = (*e)&0xffffffff00000000 | nodeElt(c) }
- func (e *nodeElt) setParent(p int16) { *e = (*e)&0xffff0000ffffffff | nodeElt(uint16(p))<<32 }
- func (e *nodeElt) setNbBits(n uint8) { *e = (*e)&0x00ffffffffffffff | nodeElt(n)<<56 }
|