github.com/zeebo/mon@v0.0.0-20211012163247-13d39bdb54fa/inthist/histogram_serialize.go (about)

     1  package inthist
     2  
     3  import (
     4  	"encoding/binary"
     5  
     6  	"github.com/zeebo/errs"
     7  	"github.com/zeebo/mon/internal/buffer"
     8  )
     9  
    10  func (h *Histogram) Serialize(dst []byte) []byte {
    11  	le := binary.LittleEndian
    12  
    13  	if cap(dst) < 128 {
    14  		dst = make([]byte, 128)
    15  	}
    16  
    17  	buf := buffer.Of(dst).Advance(8)
    18  	aidx := uintptr(0)
    19  
    20  	acount := uint8(0)
    21  	action := uint64(0)
    22  
    23  	prev := uint32(0)
    24  	skip := uint32(0)
    25  
    26  	prevBucket := ^uint(0)
    27  
    28  	bm := h.bitmap.Clone()
    29  	for {
    30  		bucket, ok := bm.Next()
    31  		if !ok {
    32  			break
    33  		}
    34  
    35  		if delta := bucket - prevBucket; delta > 1 {
    36  			skip += histEntries * uint32(delta-1)
    37  		}
    38  		prevBucket = bucket
    39  
    40  		b := h.buckets[bucket]
    41  		for entry := range b.entries[:] {
    42  			count := b.entries[entry]
    43  			if count == 0 {
    44  				skip++
    45  				continue
    46  			}
    47  
    48  			if skip > 0 {
    49  				if acount == 64 {
    50  					le.PutUint64(buf.Index8(aidx)[:], action)
    51  					aidx = buf.Pos()
    52  					buf = buf.Advance(8).Grow()
    53  					acount = 0
    54  				}
    55  
    56  				action = action>>1 | (1 << 63)
    57  				acount++
    58  
    59  				nbytes, enc := varintStats(skip)
    60  				le.PutUint64(buf.Front8()[:], enc)
    61  				buf = buf.Advance(uintptr(nbytes)).Grow()
    62  				skip = 0
    63  			}
    64  
    65  			{
    66  				if acount == 64 {
    67  					le.PutUint64(buf.Index8(aidx)[:], action)
    68  					aidx = buf.Pos()
    69  					buf = buf.Advance(8).Grow()
    70  					acount = 0
    71  				}
    72  
    73  				action = action >> 1
    74  				acount++
    75  
    76  				delta := int32(count) - int32(prev)
    77  				val := uint32((delta + delta) ^ (delta >> 31))
    78  
    79  				nbytes, enc := varintStats(val)
    80  				le.PutUint64(buf.Front8()[:], enc)
    81  				buf = buf.Advance(uintptr(nbytes)).Grow()
    82  			}
    83  
    84  			prev = count
    85  		}
    86  	}
    87  
    88  	if delta := histBuckets - prevBucket; delta > 1 {
    89  		skip += histEntries * uint32(delta-1)
    90  	}
    91  
    92  	if skip > 0 {
    93  		if acount == 64 {
    94  			le.PutUint64(buf.Index8(aidx)[:], action)
    95  			aidx = buf.Pos()
    96  			buf = buf.Advance(8).Grow()
    97  			acount = 0
    98  		}
    99  
   100  		action = action>>1 | (1 << 63)
   101  		acount++
   102  
   103  		nbytes, enc := varintStats(skip)
   104  		le.PutUint64(buf.Front8()[:], enc)
   105  		buf = buf.Advance(uintptr(nbytes)).Grow()
   106  	}
   107  
   108  	if acount > 0 {
   109  		action >>= (64 - acount) % 64
   110  		le.PutUint64(buf.Index8(aidx)[:], action)
   111  	}
   112  
   113  	return buf.Prefix()
   114  }
   115  
   116  func (h *Histogram) Load(data []byte) (err error) {
   117  	le := binary.LittleEndian
   118  	buf := buffer.OfLen(data)
   119  
   120  	b := (*histBucket)(nil)
   121  
   122  	bi := uint32(0)
   123  	entry := uint32(0)
   124  	value := uint32(0)
   125  
   126  	for buf.Remaining() > 8 {
   127  		actions := le.Uint64(buf.Front8()[:])
   128  		buf = buf.Advance(8)
   129  
   130  		for i := 0; i < 64; i++ {
   131  			var dec uint32
   132  
   133  			rem := buf.Remaining()
   134  			if rem >= 8 {
   135  				var nbytes uint8
   136  				nbytes, dec = fastVarintConsume(le.Uint64(buf.Front8()[:]))
   137  				buf = buf.Advance(uintptr(nbytes))
   138  				if buf.Pos() > buf.Cap() {
   139  					err = errs.New("invalid varint data")
   140  					goto done
   141  				}
   142  
   143  			} else if rem > 0 {
   144  				var ok bool
   145  				dec, buf, ok = safeVarintConsume(buf)
   146  				if !ok {
   147  					err = errs.New("invalid varint data")
   148  					goto done
   149  				}
   150  
   151  			} else {
   152  				goto check
   153  
   154  			}
   155  
   156  			if actions&1 != 0 {
   157  				entry += dec
   158  
   159  			} else {
   160  				delta := (dec >> 1) ^ -(dec & 1)
   161  				value += delta
   162  
   163  				if b == nil {
   164  					if int(bi) >= histBuckets {
   165  						err = errs.New("overflow number of buckets")
   166  						goto done
   167  					}
   168  
   169  					b = new(histBucket)
   170  					h.buckets[bi] = b
   171  					h.bitmap.Set(uint(bi))
   172  				}
   173  
   174  				b.entries[entry%histEntries] = value
   175  				entry++
   176  			}
   177  
   178  			if entry >= histEntries {
   179  				bi += entry / histEntries
   180  				entry %= histEntries
   181  				b = nil
   182  			}
   183  
   184  			actions >>= 1
   185  		}
   186  	}
   187  
   188  check:
   189  	if bi != histBuckets || entry != 0 || buf.Remaining() != 0 {
   190  		err = errs.New("invalid encoded data")
   191  	}
   192  
   193  done:
   194  	return err
   195  }