This repository has been archived by the owner on May 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshifty.go
483 lines (413 loc) · 10.3 KB
/
shifty.go
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
package shifty
import (
"fmt"
"strconv"
"strings"
)
/*
Kind represents the specific unsigned integer type
selected for use within instances of BitValue.
*/
type Kind uint8
/*
Kind constants define the desired bit allocation
size for an instance of BitValue.
*/
const (
_ Kind = iota // 0x0
Uint8 // 0x1; allows eight (8) bits, max val: 255
Uint16 // 0x2; allows sixteen (16) bits, max val: 65535
Uint32 // 0x3; allows thirty-two (32) bits, max val: 4294967295
)
/*
String returns the string name of the receiver instance,
each of which are literals of the underlying type which
was selected during initialization:
- uint8
- uint16
- uint32
*/
func (r Kind) String() (k string) {
k = `unknown`
switch r {
case Uint8:
k = `uint8`
case Uint16:
k = `uint16`
case Uint32:
k = `uint32`
}
return
}
/*
Size returns the bit size of the receiver. Possible (valid)
values are eight (8), sixteen (16) or thirty-two (32).
A value of zero (0) indicates the instance has not yet been
initialized (hint: see the New function).
*/
func (r Kind) Size() (size int) {
size = 0
switch r {
case Uint8:
size = 8
case Uint16:
size = 16
case Uint32:
size = 32
}
return
}
/*
BitValue contains the allocated value type, a Kind and a bit size reflecting the
allocated instance magnitude.
Shift, Unshift and Positive operations may be conducted against instances of this
type.
New instances of this type are created using the New package-level function.
*/
type BitValue struct {
k Kind // user-selected Kind
s uint8 // size (in bits: 8, 16 or 32)
v any // allocated instance (as a ptr), per Kind
m map[int]string // string names for values, optional
}
/*
Value returns the unasserted (POINTER) instance of an
interface (any) within the receiver instance. If the
value is nil, this indicates the receiver instance has
not yet been initialized (hint: see New function).
*/
func (r BitValue) Value() any {
return r.v
}
/*
NamesMap returns the instance of map[int]string found
within the receiver instance, else nil is returned.
The map is used to resolve string names to shift values
(e.g.: consts).
*/
func (r BitValue) NamesMap() map[int]string {
return r.m
}
/*
SetNamesMap assigns an instance of map[int]string to
the receiver. The instance, if non-nil, shall be used
to resolve string names to shift values (e.g.: consts).
Case is not significant in the string matching process.
*/
func (r *BitValue) SetNamesMap(m map[int]string) {
r.m = m
}
/*
Int returns the integer form of the underlying
allocated value. As only unsigned integer types
are supported in this package, the value shall
never be less than zero (0).
*/
func (r BitValue) Int() (i int) {
switch r.k {
case Uint8:
i = int(*(r.v.(*uint8)))
case Uint16:
i = int(*(r.v.(*uint16)))
case Uint32:
i = int(*(r.v.(*uint32)))
}
return
}
/*
Kind returns the instance of Kind assigned to the receiver
instance, which can be one (1) of three (3) possible values.
See the Kind constants for the complete list.
*/
func (r BitValue) Kind() Kind {
return r.k
}
/*
Shift shall left-shift the bits within the receiver to include
input value(s) x.
If any of values x are considered extremes in their magnitudes
(either zero or the maximum allowed per allocation size), one (1)
of the following shall occur:
- If value is the maximum, receiver will be clobbered with it (overwritten), i.e.: shift all
- If value is zero (0), no shift shall occur as it is illogical and not actionable
Additionally, if a maximum value is present within a variadic call
containing >1 slice, unexpected results may ensue. Generally, use
of the maximum value should be used in unary context when calling
this method.
*/
func (r BitValue) Shift(x ...any) BitValue {
for i := 0; i < len(x); i++ {
if X, ok := r.verifyShiftValue(x[i]); ok {
r.shift(X)
}
}
return r
}
/*
None is a convenience method that calls r.Unshift(r.Max()), which will
set the underlying integer value to zero (0) (allocated minimum).
*/
func (r BitValue) None() BitValue {
r.Unshift(r.Max())
return r
}
/*
All is a convenience method that calls r.Shift(r.Max()), which
will set the underlying integer value to ^uintN(0) (allocated
maximum).
*/
func (r BitValue) All() BitValue {
r.Shift(r.Max())
return r
}
/*
Unshift shall right-shift the bits within the receiver to remove
input value(s) x.
If any of values x are considered extremes in their magnitudes
(either zero or the maximum allowed per allocation size), one (1)
of the following shall occur:
- If value is the maximum, receiver will be annihilated to zero (0), i.e.: unshift all
- If value is zero (0), no unshift shall occur as it is illogical and not actionable
Additionally, if a maximum value is present within a variadic call
containing >1 slice, unexpected results may ensue. Generally, use
of the maximum value should be used in unary context when calling
this method.
*/
func (r BitValue) Unshift(x ...any) BitValue {
for i := 0; i < len(x); i++ {
if X, ok := r.verifyShiftValue(x[i]); ok {
r.unshift(X)
}
}
return r
}
/*
Positive returns a Boolean value indicative of whether input value
x's bits are set within the receiver. Negation (!) implies negative,
or 'bit not set'.
*/
func (r BitValue) Positive(x any) (posi bool) {
if X, ok := r.verifyShiftValue(x); ok {
posi = r.positive(X)
}
return
}
/*
shift is a private method used by BitValue.Shift.
*/
func (r BitValue) shift(x int) {
if r.isExtreme(x) {
r.shiftExtremes(x)
return
}
if !r.positive(x) {
switch r.k {
case Uint8:
*(r.v.(*uint8)) |= uint8(x)
case Uint16:
*(r.v.(*uint16)) |= uint16(x)
case Uint32:
*(r.v.(*uint32)) |= uint32(x)
}
}
return
}
/*
isExtreme returns a Boolean value indicative of whether
integer value x is either zero (0) or the maximum for
the underlying type.
*/
func (r BitValue) isExtreme(x int) bool {
return x == r.Max() || x == 0
}
/*
shiftExtremes handles either of two extremes: the
shift value is zero (0) or is the maximum for the
underlying type (^uintN(0), where N is a bitsize
of 8, 16 or 32).
Zero (0) is ignored. Max clobbers receiver.
The name may be somewhat misleading; the extreme
value is not shifted, rather it is set literally
and will clobber whatever value was present at
that point.
*/
func (r BitValue) shiftExtremes(x int) {
if x == r.Max() {
switch r.k {
case Uint8:
*(r.v.(*uint8)) = ^uint8(0)
case Uint16:
*(r.v.(*uint16)) = ^uint16(0)
case Uint32:
*(r.v.(*uint32)) = ^uint32(0)
}
}
}
/*
unshiftExtremes handles the shifting of the maximum
value permitted by the underlying type in negated
context. In other words, unshift max means unshift
everything.
Zero (0) is ignored.
*/
func (r BitValue) unshiftExtremes(x int) {
if x == r.Max() {
switch r.k {
case Uint8:
*(r.v.(*uint8)) = 0
case Uint16:
*(r.v.(*uint16)) = 0
case Uint32:
*(r.v.(*uint32)) = 0
}
}
}
/*
unshift is a private method used by BitValue.Unshift.
*/
func (r BitValue) unshift(x int) {
if r.isExtreme(x) {
r.unshiftExtremes(x)
return
}
if r.positive(x) {
switch r.k {
case Uint8:
*(r.v.(*uint8)) = *(r.v.(*uint8)) &^ uint8(x)
case Uint16:
*(r.v.(*uint16)) = *(r.v.(*uint16)) &^ uint16(x)
case Uint32:
*(r.v.(*uint32)) = *(r.v.(*uint32)) &^ uint32(x)
}
}
return
}
/*
positive is a private method used by BitValue.Positive.
*/
func (r BitValue) positive(x int) (posi bool) {
switch r.k {
case Uint8:
posi = *(r.v.(*uint8))&uint8(x) > 0
case Uint16:
posi = *(r.v.(*uint16))&uint16(x) > 0
case Uint32:
posi = *(r.v.(*uint32))&uint32(x) > 0
}
return
}
/*
toInt is merely a means to keep cyclomatics low in this package. this
package-level function simply asserts and casts various integer types
to a straight int instance. If value x was an int to begin with, it is
silently accepted and treated as if it were something else.
*/
func toInt(x any) (v int, ok bool) {
switch tv := x.(type) {
case int:
v = tv
ok = true
case uint8:
v = int(tv)
ok = true
case uint16:
v = int(tv)
ok = true
case uint32:
v = int(tv)
ok = true
default:
// use rcvr max in string representation
// to cast to an int. Hacky, but helpful.
if X, err := strconv.Atoi(fmt.Sprintf("%d", tv)); err == nil {
v = X
ok = true
}
}
return
}
/*
Max returns ^uintN(0), where N is a bit size of eight
(8), sixteen (16), or thirty-two (32).
The returned integer represents the largest possible
value permitted by the underlying allocated instance.
*/
func (r BitValue) Max() (max int) {
switch r.k {
case Uint8:
max = int(^uint8(0))
case Uint16:
max = int(^uint16(0))
case Uint32:
max = int(^uint32(0))
}
return
}
/*
Size returns the underlying bit size of the receiver.
*/
func (r BitValue) Size() (size int) {
return r.k.Size()
}
/*
Min returns zero (0), which indicates the lowest permitted
value within the receiver for any supported allocation.
*/
func (r BitValue) Min() (min int) {
return 0
}
/*
verifyShiftValue returns an integer and a Boolean value following
the processing of input value x, which must represent some kind of
integer. Assuming that integer is not out-of-bounds (in terms of
minimum or maximum value magnitudes permitted) for the underlying
type instance, it is returned alongside a success-indicative Boolean
value.
This method also resolves a string name to a known int bit value,
if set within the NamesMap within the receiver.
*/
func (r BitValue) verifyShiftValue(x any) (X int, ok bool) {
// if the input value was a string, try
// to resolve it to an int value ...
if str, asserted := x.(string); asserted {
x = r.strIndex(str)
}
if X, ok = toInt(x); ok {
ok = r.Min() <= X && X <= r.Max()
}
return
}
/*
strIndex returns the integer index for the specified bitvalue,
if found within the underlying names map. Otherwise, -1 is
returned.
*/
func (r BitValue) strIndex(x string) (idx int) {
idx = -1
for k, v := range r.m {
if strings.EqualFold(v, x) {
idx = k
}
}
return
}
/*
New initializes a new instance of BitValue, using Kind k as
the indicator for the desired bit allocation size. See the
Kind constants for available values.
*/
func New(k Kind) (bv BitValue) {
bv.k = k
switch k {
case Uint8:
bv.s = uint8(k.Size())
bv.v = new(uint8)
case Uint16:
bv.s = uint8(k.Size())
bv.v = new(uint16)
case Uint32:
bv.s = uint8(k.Size())
bv.v = new(uint32)
}
return
}