File: bitarray.h

package info (click to toggle)
graphviz 14.0.5-2
  • links: PTS
  • area: main
  • in suites: forky
  • size: 139,388 kB
  • sloc: ansic: 141,938; cpp: 11,957; python: 7,766; makefile: 4,043; yacc: 3,030; xml: 2,972; tcl: 2,495; sh: 1,388; objc: 1,159; java: 560; lex: 423; perl: 243; awk: 156; pascal: 139; php: 58; ruby: 49; cs: 31; sed: 1
file content (122 lines) | stat: -rw-r--r-- 3,751 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/// @file
/// @ingroup cgraph_utils
/// @brief API for compacted arrays of booleans
///
/// The straightforward way to construct a dynamic array of booleans is to
/// `calloc` an array of `bool` values. However, this wastes a lot of memory.
/// Typically 8 bits per byte, which really adds up for large arrays.
///
/// The following implements an alternative that stores 8 array elements per
/// byte. Using this over the `bool` implementation described above decreases
/// heap pressure and increases locality of reference, at the cost of a few
/// (inexpensive) shifts and masks.
///
/// As a bonus, short arrays are stored directly inline, avoiding heap
/// allocation altogether. This is essentially Small String Optimization applied
/// to a boolean array.
///
/// The above design is essentially what C++’s `std::vector<bool>` does.
///
/// This is deliberately implemented header-only so even Graphviz components
/// that do not link against cgraph can use it.

#pragma once

#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <util/alloc.h>

/// a compressed array of boolean values
///
/// Note that this complies with the zero-is-initialization idiom. That is, C99
/// zero initializing one of these (`bitarray_t b = {0}`) or `memset`ing one of
/// these to zero gives you a valid zero-length bit array.
typedef struct {
  union {
    uint8_t block[sizeof(uint8_t *)]; ///< inline storage for small arrays
    uint8_t *base; ///< start of the underlying allocated buffer
  };
  size_t size_bits; ///< extent in bits
} bitarray_t;

/// create an array of the given element length
static inline bitarray_t bitarray_new(size_t size_bits) {

  bitarray_t ba = {.size_bits = size_bits};

  // if the array is small enough, we can use inline storage
  if (size_bits <= sizeof(ba.block) * 8) {
    // nothing to be done

    // otherwise we need to heap-allocate
  } else {
    size_t capacity = size_bits / 8 + (size_bits % 8 == 0 ? 0 : 1);
    ba.base = gv_calloc(capacity, sizeof(uint8_t));
  }

  return ba;
}

/// get the value of the given element
static inline bool bitarray_get(bitarray_t self, size_t index) {
  assert(index < self.size_bits && "out of bounds access");

  // determine if this array is stored inline or not
  const uint8_t *base;
  if (self.size_bits <= sizeof(self.block) * 8) {
    base = self.block;
  } else {
    base = self.base;
  }

  return (base[index / 8] >> (index % 8)) & 1;
}

/// set or clear the value of the given element
static inline void bitarray_set(bitarray_t *self, size_t index, bool value) {
  assert(index < self->size_bits && "out of bounds access");

  // determine if this array is stored inline or not
  uint8_t *base;
  if (self->size_bits <= sizeof(self->block) * 8) {
    base = self->block;
  } else {
    base = self->base;
  }

  if (value) {
    base[index / 8] |= (uint8_t)(UINT8_C(1) << (index % 8));
  } else {
    base[index / 8] &= (uint8_t)~(UINT8_C(1) << (index % 8));
  }
}

/// clear all bits in a bit array
static inline void bitarray_clear(bitarray_t *self) {
  assert(self != NULL);

  // determine if this array is stored inline or not
  uint8_t *const base =
      self->size_bits <= sizeof(self->block) * 8 ? self->block : self->base;

  // calculate byte extent covering the array
  const size_t size = self->size_bits / 8 + (self->size_bits % 8 == 0 ? 0 : 1);

  // zero all bits
  memset(base, 0, size);
}

/// free underlying resources and leave a bit array empty
static inline void bitarray_reset(bitarray_t *self) {
  assert(self != NULL);

  // is this array stored out of line?
  if (self->size_bits > sizeof(self->block) * 8)
    free(self->base);

  *self = (bitarray_t){0};
}