ints.mx raw

   1  // Package ints is an optimised encoder for decimal numbers in ASCII format.
   2  package ints
   3  
   4  import (
   5  	_ "embed"
   6  	"io"
   7  
   8  	"smesh.lol/pkg/lol/errorf"
   9  )
  10  
  11  //go:embed base10k.txt
  12  var base10k []byte
  13  
  14  const base = 10000
  15  
  16  type Integer interface {
  17  	~int | ~int8 | ~int16 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint64
  18  }
  19  
  20  type T struct {
  21  	N uint64
  22  }
  23  
  24  func New[V Integer](n V) *T {
  25  	return &T{uint64(n)}
  26  }
  27  
  28  func (n *T) Uint64() uint64  { return n.N }
  29  func (n *T) Int64() int64    { return int64(n.N) }
  30  func (n *T) Uint16() uint16  { return uint16(n.N) }
  31  
  32  var powers = []*T{
  33  	{1},
  34  	{1_0000},
  35  	{1_0000_0000},
  36  	{1_0000_0000_0000},
  37  	{1_0000_0000_0000_0000},
  38  }
  39  
  40  const zero = '0'
  41  const nine = '9'
  42  
  43  func (n *T) Marshal(dst []byte) (b []byte) {
  44  	nn := n.N
  45  	b = dst
  46  	if n.N == 0 {
  47  		b = append(b, '0')
  48  		return
  49  	}
  50  	var i int
  51  	var trimmed bool
  52  	k := len(powers)
  53  	for k > 0 {
  54  		k--
  55  		q := n.N / powers[k].N
  56  		if !trimmed && q == 0 {
  57  			continue
  58  		}
  59  		offset := q * 4
  60  		bb := base10k[offset : offset+4]
  61  		if !trimmed {
  62  			for i = range bb {
  63  				if bb[i] != '0' {
  64  					bb = bb[i:]
  65  					trimmed = true
  66  					break
  67  				}
  68  			}
  69  		}
  70  		b = append(b, bb...)
  71  		n.N = n.N - q*powers[k].N
  72  	}
  73  	n.N = nn
  74  	return
  75  }
  76  
  77  func (n *T) Unmarshal(b []byte) (r []byte, err error) {
  78  	if len(b) < 1 {
  79  		err = errorf.E([]byte("zero length number"))
  80  		return
  81  	}
  82  	var sLen int
  83  	if b[0] == zero {
  84  		r = b[1:]
  85  		n.N = 0
  86  		return
  87  	}
  88  	for i, v := range b {
  89  		if v >= '0' && v <= '9' {
  90  			b = b[i:]
  91  			break
  92  		}
  93  	}
  94  	if len(b) == 0 {
  95  		err = io.EOF
  96  		return
  97  	}
  98  	for ; sLen < len(b) && b[sLen] >= zero && b[sLen] <= nine && b[sLen] != ','; sLen++ {
  99  	}
 100  	if sLen == 0 {
 101  		err = errorf.E([]byte("zero length number"))
 102  		return
 103  	}
 104  	if sLen > 20 {
 105  		err = errorf.E([]byte("too big number for uint64"))
 106  		return
 107  	}
 108  	r = b[sLen:]
 109  	b = b[:sLen]
 110  	n.N = uint64(b[0]) - zero
 111  	b = b[1:]
 112  	for _, ch := range b {
 113  		ch -= zero
 114  		n.N = n.N*10 + uint64(ch)
 115  	}
 116  	return
 117  }
 118