github.com/coyove/sdss@v0.0.0-20231129015646-c2ec58cca6a2/contrib/roaring/serialization_littleendian.go (about)

     1  //go:build (386 && !appengine) || (amd64 && !appengine) || (arm && !appengine) || (arm64 && !appengine) || (ppc64le && !appengine) || (mipsle && !appengine) || (mips64le && !appengine) || (mips64p32le && !appengine) || (wasm && !appengine)
     2  // +build 386,!appengine amd64,!appengine arm,!appengine arm64,!appengine ppc64le,!appengine mipsle,!appengine mips64le,!appengine mips64p32le,!appengine wasm,!appengine
     3  
     4  package roaring
     5  
     6  import (
     7  	"encoding/binary"
     8  	"errors"
     9  	"io"
    10  	"reflect"
    11  	"runtime"
    12  	"unsafe"
    13  )
    14  
    15  func (ac *arrayContainer) writeTo(stream io.Writer) (int, error) {
    16  	buf := uint16SliceAsByteSlice(ac.content)
    17  	return stream.Write(buf)
    18  }
    19  
    20  func (bc *bitmapContainer) writeTo(stream io.Writer) (int, error) {
    21  	if bc.cardinality <= arrayDefaultMaxSize {
    22  		return 0, errors.New("refusing to write bitmap container with cardinality of array container")
    23  	}
    24  	buf := uint64SliceAsByteSlice(bc.bitmap)
    25  	return stream.Write(buf)
    26  }
    27  
    28  func uint64SliceAsByteSlice(slice []uint64) []byte {
    29  	// make a new slice header
    30  	header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
    31  
    32  	// update its capacity and length
    33  	header.Len *= 8
    34  	header.Cap *= 8
    35  
    36  	// instantiate result and use KeepAlive so data isn't unmapped.
    37  	result := *(*[]byte)(unsafe.Pointer(&header))
    38  	runtime.KeepAlive(&slice)
    39  
    40  	// return it
    41  	return result
    42  }
    43  
    44  func uint16SliceAsByteSlice(slice []uint16) []byte {
    45  	// make a new slice header
    46  	header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
    47  
    48  	// update its capacity and length
    49  	header.Len *= 2
    50  	header.Cap *= 2
    51  
    52  	// instantiate result and use KeepAlive so data isn't unmapped.
    53  	result := *(*[]byte)(unsafe.Pointer(&header))
    54  	runtime.KeepAlive(&slice)
    55  
    56  	// return it
    57  	return result
    58  }
    59  
    60  func interval16SliceAsByteSlice(slice []interval16) []byte {
    61  	// make a new slice header
    62  	header := *(*reflect.SliceHeader)(unsafe.Pointer(&slice))
    63  
    64  	// update its capacity and length
    65  	header.Len *= 4
    66  	header.Cap *= 4
    67  
    68  	// instantiate result and use KeepAlive so data isn't unmapped.
    69  	result := *(*[]byte)(unsafe.Pointer(&header))
    70  	runtime.KeepAlive(&slice)
    71  
    72  	// return it
    73  	return result
    74  }
    75  
    76  func (bc *bitmapContainer) asLittleEndianByteSlice() []byte {
    77  	return uint64SliceAsByteSlice(bc.bitmap)
    78  }
    79  
    80  // Deserialization code follows
    81  
    82  ////
    83  // These methods (byteSliceAsUint16Slice,...) do not make copies,
    84  // they are pointer-based (unsafe). The caller is responsible to
    85  // ensure that the input slice does not get garbage collected, deleted
    86  // or modified while you hold the returned slince.
    87  ////
    88  func byteSliceAsUint16Slice(slice []byte) (result []uint16) { // here we create a new slice holder
    89  	if len(slice)%2 != 0 {
    90  		panic("Slice size should be divisible by 2")
    91  	}
    92  	// reference: https://go101.org/article/unsafe.html
    93  
    94  	// make a new slice header
    95  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
    96  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
    97  
    98  	// transfer the data from the given slice to a new variable (our result)
    99  	rHeader.Data = bHeader.Data
   100  	rHeader.Len = bHeader.Len / 2
   101  	rHeader.Cap = bHeader.Cap / 2
   102  
   103  	// instantiate result and use KeepAlive so data isn't unmapped.
   104  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   105  
   106  	// return result
   107  	return
   108  }
   109  
   110  func byteSliceAsUint64Slice(slice []byte) (result []uint64) {
   111  	if len(slice)%8 != 0 {
   112  		panic("Slice size should be divisible by 8")
   113  	}
   114  	// reference: https://go101.org/article/unsafe.html
   115  
   116  	// make a new slice header
   117  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   118  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   119  
   120  	// transfer the data from the given slice to a new variable (our result)
   121  	rHeader.Data = bHeader.Data
   122  	rHeader.Len = bHeader.Len / 8
   123  	rHeader.Cap = bHeader.Cap / 8
   124  
   125  	// instantiate result and use KeepAlive so data isn't unmapped.
   126  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   127  
   128  	// return result
   129  	return
   130  }
   131  
   132  func byteSliceAsInterval16Slice(slice []byte) (result []interval16) {
   133  	if len(slice)%4 != 0 {
   134  		panic("Slice size should be divisible by 4")
   135  	}
   136  	// reference: https://go101.org/article/unsafe.html
   137  
   138  	// make a new slice header
   139  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   140  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   141  
   142  	// transfer the data from the given slice to a new variable (our result)
   143  	rHeader.Data = bHeader.Data
   144  	rHeader.Len = bHeader.Len / 4
   145  	rHeader.Cap = bHeader.Cap / 4
   146  
   147  	// instantiate result and use KeepAlive so data isn't unmapped.
   148  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   149  
   150  	// return result
   151  	return
   152  }
   153  
   154  func byteSliceAsContainerSlice(slice []byte) (result []container) {
   155  	var c container
   156  	containerSize := int(unsafe.Sizeof(c))
   157  
   158  	if len(slice)%containerSize != 0 {
   159  		panic("Slice size should be divisible by unsafe.Sizeof(container)")
   160  	}
   161  	// reference: https://go101.org/article/unsafe.html
   162  
   163  	// make a new slice header
   164  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   165  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   166  
   167  	// transfer the data from the given slice to a new variable (our result)
   168  	rHeader.Data = bHeader.Data
   169  	rHeader.Len = bHeader.Len / containerSize
   170  	rHeader.Cap = bHeader.Cap / containerSize
   171  
   172  	// instantiate result and use KeepAlive so data isn't unmapped.
   173  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   174  
   175  	// return result
   176  	return
   177  }
   178  
   179  func byteSliceAsBitsetSlice(slice []byte) (result []bitmapContainer) {
   180  	bitsetSize := int(unsafe.Sizeof(bitmapContainer{}))
   181  	if len(slice)%bitsetSize != 0 {
   182  		panic("Slice size should be divisible by unsafe.Sizeof(bitmapContainer)")
   183  	}
   184  	// reference: https://go101.org/article/unsafe.html
   185  
   186  	// make a new slice header
   187  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   188  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   189  
   190  	// transfer the data from the given slice to a new variable (our result)
   191  	rHeader.Data = bHeader.Data
   192  	rHeader.Len = bHeader.Len / bitsetSize
   193  	rHeader.Cap = bHeader.Cap / bitsetSize
   194  
   195  	// instantiate result and use KeepAlive so data isn't unmapped.
   196  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   197  
   198  	// return result
   199  	return
   200  }
   201  
   202  func byteSliceAsArraySlice(slice []byte) (result []arrayContainer) {
   203  	arraySize := int(unsafe.Sizeof(arrayContainer{}))
   204  	if len(slice)%arraySize != 0 {
   205  		panic("Slice size should be divisible by unsafe.Sizeof(arrayContainer)")
   206  	}
   207  	// reference: https://go101.org/article/unsafe.html
   208  
   209  	// make a new slice header
   210  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   211  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   212  
   213  	// transfer the data from the given slice to a new variable (our result)
   214  	rHeader.Data = bHeader.Data
   215  	rHeader.Len = bHeader.Len / arraySize
   216  	rHeader.Cap = bHeader.Cap / arraySize
   217  
   218  	// instantiate result and use KeepAlive so data isn't unmapped.
   219  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   220  
   221  	// return result
   222  	return
   223  }
   224  
   225  func byteSliceAsRun16Slice(slice []byte) (result []runContainer16) {
   226  	run16Size := int(unsafe.Sizeof(runContainer16{}))
   227  	if len(slice)%run16Size != 0 {
   228  		panic("Slice size should be divisible by unsafe.Sizeof(runContainer16)")
   229  	}
   230  	// reference: https://go101.org/article/unsafe.html
   231  
   232  	// make a new slice header
   233  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   234  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   235  
   236  	// transfer the data from the given slice to a new variable (our result)
   237  	rHeader.Data = bHeader.Data
   238  	rHeader.Len = bHeader.Len / run16Size
   239  	rHeader.Cap = bHeader.Cap / run16Size
   240  
   241  	// instantiate result and use KeepAlive so data isn't unmapped.
   242  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   243  
   244  	// return result
   245  	return
   246  }
   247  
   248  func byteSliceAsBoolSlice(slice []byte) (result []bool) {
   249  	boolSize := int(unsafe.Sizeof(true))
   250  	if len(slice)%boolSize != 0 {
   251  		panic("Slice size should be divisible by unsafe.Sizeof(bool)")
   252  	}
   253  	// reference: https://go101.org/article/unsafe.html
   254  
   255  	// make a new slice header
   256  	bHeader := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
   257  	rHeader := (*reflect.SliceHeader)(unsafe.Pointer(&result))
   258  
   259  	// transfer the data from the given slice to a new variable (our result)
   260  	rHeader.Data = bHeader.Data
   261  	rHeader.Len = bHeader.Len / boolSize
   262  	rHeader.Cap = bHeader.Cap / boolSize
   263  
   264  	// instantiate result and use KeepAlive so data isn't unmapped.
   265  	runtime.KeepAlive(&slice) // it is still crucial, GC can free it)
   266  
   267  	// return result
   268  	return
   269  }
   270  
   271  // FrozenView creates a static view of a serialized bitmap stored in buf.
   272  // It uses CRoaring's frozen bitmap format.
   273  //
   274  // The format specification is available here:
   275  // https://github.com/RoaringBitmap/CRoaring/blob/2c867e9f9c9e2a3a7032791f94c4c7ae3013f6e0/src/roaring.c#L2756-L2783
   276  //
   277  // The provided byte array (buf) is expected to be a constant.
   278  // The function makes the best effort attempt not to copy data.
   279  // Only little endian is supported. The function will err if it detects a big
   280  // endian serialized file.
   281  // You should take care not to modify buff as it will likely result in
   282  // unexpected program behavior.
   283  // If said buffer comes from a memory map, it's advisable to give it read
   284  // only permissions, either at creation or by calling Mprotect from the
   285  // golang.org/x/sys/unix package.
   286  //
   287  // Resulting bitmaps are effectively immutable in the following sense:
   288  // a copy-on-write marker is used so that when you modify the resulting
   289  // bitmap, copies of selected data (containers) are made.
   290  // You should *not* change the copy-on-write status of the resulting
   291  // bitmaps (SetCopyOnWrite).
   292  //
   293  // If buf becomes unavailable, then a bitmap created with
   294  // FromBuffer would be effectively broken. Furthermore, any
   295  // bitmap derived from this bitmap (e.g., via Or, And) might
   296  // also be broken. Thus, before making buf unavailable, you should
   297  // call CloneCopyOnWriteContainers on all such bitmaps.
   298  //
   299  func (rb *Bitmap) FrozenView(buf []byte) error {
   300  	return rb.highlowcontainer.frozenView(buf)
   301  }
   302  
   303  /* Verbatim specification from CRoaring.
   304   *
   305   * FROZEN SERIALIZATION FORMAT DESCRIPTION
   306   *
   307   * -- (beginning must be aligned by 32 bytes) --
   308   * <bitset_data> uint64_t[BITSET_CONTAINER_SIZE_IN_WORDS * num_bitset_containers]
   309   * <run_data>    rle16_t[total number of rle elements in all run containers]
   310   * <array_data>  uint16_t[total number of array elements in all array containers]
   311   * <keys>        uint16_t[num_containers]
   312   * <counts>      uint16_t[num_containers]
   313   * <typecodes>   uint8_t[num_containers]
   314   * <header>      uint32_t
   315   *
   316   * <header> is a 4-byte value which is a bit union of FROZEN_COOKIE (15 bits)
   317   * and the number of containers (17 bits).
   318   *
   319   * <counts> stores number of elements for every container.
   320   * Its meaning depends on container type.
   321   * For array and bitset containers, this value is the container cardinality minus one.
   322   * For run container, it is the number of rle_t elements (n_runs).
   323   *
   324   * <bitset_data>,<array_data>,<run_data> are flat arrays of elements of
   325   * all containers of respective type.
   326   *
   327   * <*_data> and <keys> are kept close together because they are not accessed
   328   * during deserilization. This may reduce IO in case of large mmaped bitmaps.
   329   * All members have their native alignments during deserilization except <header>,
   330   * which is not guaranteed to be aligned by 4 bytes.
   331   */
   332  const FROZEN_COOKIE = 13766
   333  
   334  var (
   335  	FrozenBitmapInvalidCookie   = errors.New("header does not contain the FROZEN_COOKIE")
   336  	FrozenBitmapBigEndian       = errors.New("loading big endian frozen bitmaps is not supported")
   337  	FrozenBitmapIncomplete      = errors.New("input buffer too small to contain a frozen bitmap")
   338  	FrozenBitmapOverpopulated   = errors.New("too many containers")
   339  	FrozenBitmapUnexpectedData  = errors.New("spurious data in input")
   340  	FrozenBitmapInvalidTypecode = errors.New("unrecognized typecode")
   341  	FrozenBitmapBufferTooSmall  = errors.New("buffer too small")
   342  )
   343  
   344  func (ra *roaringArray) frozenView(buf []byte) error {
   345  	if len(buf) < 4 {
   346  		return FrozenBitmapIncomplete
   347  	}
   348  
   349  	headerBE := binary.BigEndian.Uint32(buf[len(buf)-4:])
   350  	if headerBE&0x7fff == FROZEN_COOKIE {
   351  		return FrozenBitmapBigEndian
   352  	}
   353  
   354  	header := binary.LittleEndian.Uint32(buf[len(buf)-4:])
   355  	buf = buf[:len(buf)-4]
   356  
   357  	if header&0x7fff != FROZEN_COOKIE {
   358  		return FrozenBitmapInvalidCookie
   359  	}
   360  
   361  	nCont := int(header >> 15)
   362  	if nCont > (1 << 16) {
   363  		return FrozenBitmapOverpopulated
   364  	}
   365  
   366  	// 1 byte per type, 2 bytes per key, 2 bytes per count.
   367  	if len(buf) < 5*nCont {
   368  		return FrozenBitmapIncomplete
   369  	}
   370  
   371  	types := buf[len(buf)-nCont:]
   372  	buf = buf[:len(buf)-nCont]
   373  
   374  	counts := byteSliceAsUint16Slice(buf[len(buf)-2*nCont:])
   375  	buf = buf[:len(buf)-2*nCont]
   376  
   377  	keys := byteSliceAsUint16Slice(buf[len(buf)-2*nCont:])
   378  	buf = buf[:len(buf)-2*nCont]
   379  
   380  	nBitmap, nArray, nRun := 0, 0, 0
   381  	nArrayEl, nRunEl := 0, 0
   382  	for i, t := range types {
   383  		switch t {
   384  		case 1:
   385  			nBitmap++
   386  		case 2:
   387  			nArray++
   388  			nArrayEl += int(counts[i]) + 1
   389  		case 3:
   390  			nRun++
   391  			nRunEl += int(counts[i])
   392  		default:
   393  			return FrozenBitmapInvalidTypecode
   394  		}
   395  	}
   396  
   397  	if len(buf) < (1<<13)*nBitmap+4*nRunEl+2*nArrayEl {
   398  		return FrozenBitmapIncomplete
   399  	}
   400  
   401  	bitsetsArena := byteSliceAsUint64Slice(buf[:(1<<13)*nBitmap])
   402  	buf = buf[(1<<13)*nBitmap:]
   403  
   404  	runsArena := byteSliceAsInterval16Slice(buf[:4*nRunEl])
   405  	buf = buf[4*nRunEl:]
   406  
   407  	arraysArena := byteSliceAsUint16Slice(buf[:2*nArrayEl])
   408  	buf = buf[2*nArrayEl:]
   409  
   410  	if len(buf) != 0 {
   411  		return FrozenBitmapUnexpectedData
   412  	}
   413  
   414  	var c container
   415  	containersSz := int(unsafe.Sizeof(c))*nCont
   416  	bitsetsSz := int(unsafe.Sizeof(bitmapContainer{}))*nBitmap
   417  	arraysSz := int(unsafe.Sizeof(arrayContainer{}))*nArray
   418  	runsSz := int(unsafe.Sizeof(runContainer16{}))*nRun
   419  	needCOWSz := int(unsafe.Sizeof(true))*nCont
   420  
   421  	bitmapArenaSz := containersSz + bitsetsSz + arraysSz + runsSz + needCOWSz
   422  	bitmapArena := make([]byte, bitmapArenaSz)
   423  
   424  	containers := byteSliceAsContainerSlice(bitmapArena[:containersSz])
   425  	bitmapArena = bitmapArena[containersSz:]
   426  
   427  	bitsets := byteSliceAsBitsetSlice(bitmapArena[:bitsetsSz])
   428  	bitmapArena = bitmapArena[bitsetsSz:]
   429  
   430  	arrays := byteSliceAsArraySlice(bitmapArena[:arraysSz])
   431  	bitmapArena = bitmapArena[arraysSz:]
   432  
   433  	runs := byteSliceAsRun16Slice(bitmapArena[:runsSz])
   434  	bitmapArena = bitmapArena[runsSz:]
   435  
   436  	needCOW := byteSliceAsBoolSlice(bitmapArena)
   437  
   438  	iBitset, iArray, iRun := 0, 0, 0
   439  	for i, t := range types {
   440  		needCOW[i] = true
   441  
   442  		switch t {
   443  		case 1:
   444  			containers[i] = &bitsets[iBitset]
   445  			bitsets[iBitset].cardinality = int(counts[i]) + 1
   446  			bitsets[iBitset].bitmap = bitsetsArena[:1024]
   447  			bitsetsArena = bitsetsArena[1024:]
   448  			iBitset++
   449  		case 2:
   450  			containers[i] = &arrays[iArray]
   451  			sz := int(counts[i]) + 1
   452  			arrays[iArray].content = arraysArena[:sz]
   453  			arraysArena = arraysArena[sz:]
   454  			iArray++
   455  		case 3:
   456  			containers[i] = &runs[iRun]
   457  			runs[iRun].iv = runsArena[:counts[i]]
   458  			runsArena = runsArena[counts[i]:]
   459  			iRun++
   460  		}
   461  	}
   462  
   463  	// Not consuming the full input is a bug.
   464  	if iBitset != nBitmap || len(bitsetsArena) != 0 ||
   465  		iArray != nArray || len(arraysArena) != 0 ||
   466  		iRun != nRun || len(runsArena) != 0 {
   467  		panic("we missed something")
   468  	}
   469  
   470  	ra.keys = keys
   471  	ra.containers = containers
   472  	ra.needCopyOnWrite = needCOW
   473  	ra.copyOnWrite = true
   474  
   475  	return nil
   476  }
   477  
   478  func (bm *Bitmap) GetFrozenSizeInBytes() uint64 {
   479  	nBits, nArrayEl, nRunEl := uint64(0), uint64(0), uint64(0)
   480  	for _, c := range bm.highlowcontainer.containers {
   481  		switch v := c.(type) {
   482  		case *bitmapContainer:
   483  			nBits++
   484  		case *arrayContainer:
   485  			nArrayEl += uint64(len(v.content))
   486  		case *runContainer16:
   487  			nRunEl += uint64(len(v.iv))
   488  		}
   489  	}
   490  	return 4 + 5*uint64(len(bm.highlowcontainer.containers)) +
   491  		(nBits << 13) + 2*nArrayEl + 4*nRunEl
   492  }
   493  
   494  func (bm *Bitmap) Freeze() ([]byte, error) {
   495  	sz := bm.GetFrozenSizeInBytes()
   496  	buf := make([]byte, sz)
   497  	_, err := bm.FreezeTo(buf)
   498  	return buf, err
   499  }
   500  
   501  func (bm *Bitmap) FreezeTo(buf []byte) (int, error) {
   502  	containers := bm.highlowcontainer.containers
   503  	nCont := len(containers)
   504  
   505  	nBits, nArrayEl, nRunEl := 0, 0, 0
   506  	for _, c := range containers {
   507  		switch v := c.(type) {
   508  		case *bitmapContainer:
   509  			nBits++
   510  		case *arrayContainer:
   511  			nArrayEl += len(v.content)
   512  		case *runContainer16:
   513  			nRunEl += len(v.iv)
   514  		}
   515  	}
   516  
   517  	serialSize := 4 + 5*nCont + (1<<13)*nBits + 4*nRunEl + 2*nArrayEl
   518  	if len(buf) < serialSize {
   519  		return 0, FrozenBitmapBufferTooSmall
   520  	}
   521  
   522  	bitsArena := byteSliceAsUint64Slice(buf[:(1<<13)*nBits])
   523  	buf = buf[(1<<13)*nBits:]
   524  
   525  	runsArena := byteSliceAsInterval16Slice(buf[:4*nRunEl])
   526  	buf = buf[4*nRunEl:]
   527  
   528  	arraysArena := byteSliceAsUint16Slice(buf[:2*nArrayEl])
   529  	buf = buf[2*nArrayEl:]
   530  
   531  	keys := byteSliceAsUint16Slice(buf[:2*nCont])
   532  	buf = buf[2*nCont:]
   533  
   534  	counts := byteSliceAsUint16Slice(buf[:2*nCont])
   535  	buf = buf[2*nCont:]
   536  
   537  	types := buf[:nCont]
   538  	buf = buf[nCont:]
   539  
   540  	header := uint32(FROZEN_COOKIE | (nCont << 15))
   541  	binary.LittleEndian.PutUint32(buf[:4], header)
   542  
   543  	copy(keys, bm.highlowcontainer.keys[:])
   544  
   545  	for i, c := range containers {
   546  		switch v := c.(type) {
   547  		case *bitmapContainer:
   548  			copy(bitsArena, v.bitmap)
   549  			bitsArena = bitsArena[1024:]
   550  			counts[i] = uint16(v.cardinality - 1)
   551  			types[i] = 1
   552  		case *arrayContainer:
   553  			copy(arraysArena, v.content)
   554  			arraysArena = arraysArena[len(v.content):]
   555  			elems := len(v.content)
   556  			counts[i] = uint16(elems - 1)
   557  			types[i] = 2
   558  		case *runContainer16:
   559  			copy(runsArena, v.iv)
   560  			runs := len(v.iv)
   561  			runsArena = runsArena[runs:]
   562  			counts[i] = uint16(runs)
   563  			types[i] = 3
   564  		}
   565  	}
   566  
   567  	return serialSize, nil
   568  }
   569  
   570  func (bm *Bitmap) WriteFrozenTo(wr io.Writer) (int, error) {
   571  	// FIXME: this is a naive version that iterates 4 times through the
   572  	// containers and allocates 3*len(containers) bytes; it's quite likely
   573  	// it can be done more efficiently.
   574  	containers := bm.highlowcontainer.containers
   575  	written := 0
   576  
   577  	for _, c := range containers {
   578  		c, ok := c.(*bitmapContainer)
   579  		if !ok {
   580  			continue
   581  		}
   582  		n, err := wr.Write(uint64SliceAsByteSlice(c.bitmap))
   583  		written += n
   584  		if err != nil {
   585  			return written, err
   586  		}
   587  	}
   588  
   589  	for _, c := range containers {
   590  		c, ok := c.(*runContainer16)
   591  		if !ok {
   592  			continue
   593  		}
   594  		n, err := wr.Write(interval16SliceAsByteSlice(c.iv))
   595  		written += n
   596  		if err != nil {
   597  			return written, err
   598  		}
   599  	}
   600  
   601  	for _, c := range containers {
   602  		c, ok := c.(*arrayContainer)
   603  		if !ok {
   604  			continue
   605  		}
   606  		n, err := wr.Write(uint16SliceAsByteSlice(c.content))
   607  		written += n
   608  		if err != nil {
   609  			return written, err
   610  		}
   611  	}
   612  
   613  	n, err := wr.Write(uint16SliceAsByteSlice(bm.highlowcontainer.keys))
   614  	written += n
   615  	if err != nil {
   616  		return written, err
   617  	}
   618  
   619  	countTypeBuf := make([]byte, 3*len(containers))
   620  	counts := byteSliceAsUint16Slice(countTypeBuf[:2*len(containers)])
   621  	types := countTypeBuf[2*len(containers):]
   622  
   623  	for i, c := range containers {
   624  		switch c := c.(type) {
   625  		case *bitmapContainer:
   626  			counts[i] = uint16(c.cardinality - 1)
   627  			types[i] = 1
   628  		case *arrayContainer:
   629  			elems := len(c.content)
   630  			counts[i] = uint16(elems - 1)
   631  			types[i] = 2
   632  		case *runContainer16:
   633  			runs := len(c.iv)
   634  			counts[i] = uint16(runs)
   635  			types[i] = 3
   636  		}
   637  	}
   638  
   639  	n, err = wr.Write(countTypeBuf)
   640  	written += n
   641  	if err != nil {
   642  		return written, err
   643  	}
   644  
   645  	header := uint32(FROZEN_COOKIE | (len(containers) << 15))
   646  	if err := binary.Write(wr, binary.LittleEndian, header); err != nil {
   647  		return written, err
   648  	}
   649  	written += 4
   650  
   651  	return written, nil
   652  }