gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/internal/grouping.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package internal
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/base64"
    25  	"encoding/binary"
    26  	"fmt"
    27  	"sort"
    28  )
    29  
    30  // Groupings maintain labels to identify membership to one or more groups.
    31  // Labels are implemented as subsets of integers from 0
    32  // up to an excluded maximum, where the integers represent the groups.
    33  // Assumptions:
    34  //  - most labels are for one group or very few
    35  //  - a few labels are sparse with more groups in them
    36  //  - very few comprise the universe of all groups
    37  type Groupings struct {
    38  	n               uint
    39  	maxGroup        uint16
    40  	bitsetThreshold uint16
    41  }
    42  
    43  // NewGroupings creates a new Groupings supporting labels for membership
    44  // to up n groups. n must be a positive multiple of 16 and <=65536.
    45  func NewGroupings(n int) (*Groupings, error) {
    46  	if n <= 0 || n > 65536 {
    47  		return nil, fmt.Errorf("n=%d groups is outside of valid range (0, 65536]", n)
    48  	}
    49  	if n%16 != 0 {
    50  		return nil, fmt.Errorf("n=%d groups is not a multiple of 16", n)
    51  	}
    52  	return &Groupings{n: uint(n), bitsetThreshold: uint16(n / 16)}, nil
    53  }
    54  
    55  // N returns up to how many groups are supported.
    56  // That is the value that was passed to NewGroupings.
    57  func (gr *Groupings) N() int {
    58  	return int(gr.n)
    59  }
    60  
    61  // WithinRange checks whether group is within the admissible range for
    62  // labeling otherwise it returns an error.
    63  func (gr *Groupings) WithinRange(group uint16) error {
    64  	if uint(group) >= gr.n {
    65  		return fmt.Errorf("group exceeds admissible maximum: %d >= %d", group, gr.n)
    66  	}
    67  	return nil
    68  }
    69  
    70  type Grouping struct {
    71  	size  uint16
    72  	elems []uint16
    73  }
    74  
    75  func (g Grouping) Copy() Grouping {
    76  	elems2 := make([]uint16, len(g.elems), cap(g.elems))
    77  	copy(elems2[:], g.elems[:])
    78  	g.elems = elems2
    79  	return g
    80  }
    81  
    82  // search locates group among the sorted Grouping elements, it returns:
    83  //  * true if found
    84  //  * false if not found
    85  //  * the index at which group should be inserted to keep the
    86  //    elements sorted if not found and the bit-set representation is not in use
    87  func (gr *Groupings) search(g *Grouping, group uint16) (found bool, j uint16) {
    88  	if g.size > gr.bitsetThreshold {
    89  		return bitsetContains(g, group), 0
    90  	}
    91  	j = uint16(sort.Search(int(g.size), func(i int) bool { return g.elems[i] >= group }))
    92  	if j < g.size && g.elems[j] == group {
    93  		return true, 0
    94  	}
    95  	return false, j
    96  }
    97  
    98  func bitsetContains(g *Grouping, group uint16) bool {
    99  	return (g.elems[group/16] & (1 << (group % 16))) != 0
   100  }
   101  
   102  // AddTo adds the given group to the grouping.
   103  func (gr *Groupings) AddTo(g *Grouping, group uint16) error {
   104  	if err := gr.WithinRange(group); err != nil {
   105  		return err
   106  	}
   107  	if group > gr.maxGroup {
   108  		gr.maxGroup = group
   109  	}
   110  	if g.size == 0 {
   111  		g.size = 1
   112  		g.elems = []uint16{group}
   113  		return nil
   114  	}
   115  	found, j := gr.search(g, group)
   116  	if found {
   117  		return nil
   118  	}
   119  	newsize := g.size + 1
   120  	if newsize > gr.bitsetThreshold {
   121  		// switching to a bit-set representation after the size point
   122  		// where the space cost is the same, the representation uses
   123  		// bitsetThreshold-many 16-bits words stored in elems.
   124  		// We don't always use the bit-set representation because
   125  		// * we expect small groupings and iteration to be common,
   126  		//   iteration is more costly over the bit-set representation
   127  		// * serialization matches more or less what we do in memory,
   128  		//   so again is more efficient for small groupings in the
   129  		//   extensive representation.
   130  		if g.size == gr.bitsetThreshold {
   131  			prevelems := g.elems
   132  			g.elems = make([]uint16, gr.bitsetThreshold)
   133  			for _, e := range prevelems {
   134  				bitsetAdd(g, e)
   135  			}
   136  		}
   137  		g.size = newsize
   138  		bitsetAdd(g, group)
   139  		return nil
   140  	}
   141  	var newelems []uint16
   142  	if int(g.size) == cap(g.elems) {
   143  		newelems = make([]uint16, newsize, cap(g.elems)*2)
   144  		copy(newelems, g.elems[:j])
   145  	} else {
   146  		newelems = g.elems[:newsize]
   147  	}
   148  	if j < g.size {
   149  		copy(newelems[j+1:], g.elems[j:])
   150  	}
   151  	// inserting new group at j index keeping the elements sorted
   152  	newelems[j] = group
   153  	g.size = newsize
   154  	g.elems = newelems
   155  	return nil
   156  }
   157  
   158  func bitsetAdd(g *Grouping, group uint16) {
   159  	g.elems[group/16] |= 1 << (group % 16)
   160  }
   161  
   162  // Contains returns whether the given group is a member of the grouping.
   163  func (gr *Groupings) Contains(g *Grouping, group uint16) bool {
   164  	found, _ := gr.search(g, group)
   165  	return found
   166  }
   167  
   168  // Serialize produces a string encoding the given integers.
   169  func Serialize(elems []uint16) string {
   170  	b := bytes.NewBuffer(make([]byte, 0, len(elems)*2))
   171  	binary.Write(b, binary.LittleEndian, elems)
   172  	return base64.RawURLEncoding.EncodeToString(b.Bytes())
   173  }
   174  
   175  // Serialize produces a string representing the grouping label.
   176  func (gr *Groupings) Serialize(g *Grouping) string {
   177  	// groupings are serialized as:
   178  	//  * the actual element groups if there are up to
   179  	//    bitsetThreshold elements: elems[0], elems[1], ...
   180  	//  * otherwise the number of elements, followed by the bitset
   181  	//    representation comprised of bitsetThreshold-many 16-bits words
   182  	//    (stored using elems as well)
   183  	if g.size > gr.bitsetThreshold {
   184  		return gr.bitsetSerialize(g)
   185  	}
   186  	return Serialize(g.elems)
   187  }
   188  
   189  func (gr *Groupings) bitsetSerialize(g *Grouping) string {
   190  	b := bytes.NewBuffer(make([]byte, 0, (gr.bitsetThreshold+1)*2))
   191  	binary.Write(b, binary.LittleEndian, g.size)
   192  	binary.Write(b, binary.LittleEndian, g.elems)
   193  	return base64.RawURLEncoding.EncodeToString(b.Bytes())
   194  }
   195  
   196  const errSerializedLabelFmt = "invalid serialized grouping label: %v"
   197  
   198  // Deserialize reconstructs a grouping out of the serialized label.
   199  func (gr *Groupings) Deserialize(label string) (*Grouping, error) {
   200  	b, err := base64.RawURLEncoding.DecodeString(label)
   201  	if err != nil {
   202  		return nil, fmt.Errorf(errSerializedLabelFmt, err)
   203  	}
   204  	if len(b)%2 != 0 {
   205  		return nil, fmt.Errorf(errSerializedLabelFmt, "not divisible into 16-bits words")
   206  	}
   207  	m := len(b) / 2
   208  	var g Grouping
   209  	if m == int(gr.bitsetThreshold+1) {
   210  		// deserialize number of elements + bitset representation
   211  		// comprising bitsetThreshold-many 16-bits words
   212  		return gr.bitsetDeserialize(&g, b)
   213  	}
   214  	if m > int(gr.bitsetThreshold) {
   215  		return nil, fmt.Errorf(errSerializedLabelFmt, "too large")
   216  	}
   217  	g.size = uint16(m)
   218  	esz := uint16(1)
   219  	for esz < g.size {
   220  		esz *= 2
   221  	}
   222  	g.elems = make([]uint16, g.size, esz)
   223  	binary.Read(bytes.NewBuffer(b), binary.LittleEndian, g.elems)
   224  	for i, e := range g.elems {
   225  		if e > gr.maxGroup {
   226  			return nil, fmt.Errorf(errSerializedLabelFmt, "element larger than maximum group")
   227  		}
   228  		if i > 0 && g.elems[i-1] >= e {
   229  			return nil, fmt.Errorf(errSerializedLabelFmt, "not sorted")
   230  		}
   231  	}
   232  	return &g, nil
   233  }
   234  
   235  func (gr *Groupings) bitsetDeserialize(g *Grouping, b []byte) (*Grouping, error) {
   236  	buf := bytes.NewBuffer(b)
   237  	binary.Read(buf, binary.LittleEndian, &g.size)
   238  	if g.size > gr.maxGroup+1 {
   239  		return nil, fmt.Errorf(errSerializedLabelFmt, "bitset size cannot be possibly larger than maximum group plus 1")
   240  	}
   241  	if g.size <= gr.bitsetThreshold {
   242  		// should not have used a bitset repr for so few elements
   243  		return nil, fmt.Errorf(errSerializedLabelFmt, "bitset for too few elements")
   244  	}
   245  	g.elems = make([]uint16, gr.bitsetThreshold)
   246  	binary.Read(buf, binary.LittleEndian, g.elems)
   247  	return g, nil
   248  }
   249  
   250  // Iter iterates over the groups in the grouping and calls f with each of
   251  // them. If f returns an error Iter immediately returns with it.
   252  func (gr *Groupings) Iter(g *Grouping, f func(group uint16) error) error {
   253  	if g.size > gr.bitsetThreshold {
   254  		return gr.bitsetIter(g, f)
   255  	}
   256  	for _, e := range g.elems {
   257  		if err := f(e); err != nil {
   258  			return err
   259  		}
   260  	}
   261  	return nil
   262  }
   263  
   264  func (gr *Groupings) bitsetIter(g *Grouping, f func(group uint16) error) error {
   265  	c := g.size
   266  	for i := uint16(0); i <= gr.maxGroup/16; i++ {
   267  		w := g.elems[i]
   268  		if w == 0 {
   269  			continue
   270  		}
   271  		for j := uint16(0); w != 0; j++ {
   272  			if w&1 != 0 {
   273  				if err := f(i*16 + j); err != nil {
   274  					return err
   275  				}
   276  				c--
   277  				if c == 0 {
   278  					// found all elements
   279  					return nil
   280  				}
   281  			}
   282  			w >>= 1
   283  		}
   284  	}
   285  	return nil
   286  }