bit-sequence 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +29 -0
- package/.github/workflows/go.yml +47 -0
- package/.github/workflows/test-and-release.yml +56 -0
- package/CHANGELOG.md +9 -0
- package/README.md +33 -6
- package/go/bitsequence.go +58 -0
- package/go/bitsequence_test.go +111 -0
- package/go/go.mod +3 -0
- package/js/bit-sequence.js +7 -3
- package/js/test.js +87 -76
- package/package.json +121 -3
- package/test-fixture.csv +19748 -0
- package/tsconfig.json +37 -0
- package/types/bit-sequence.d.ts +8 -0
- package/types/bit-sequence.d.ts.map +1 -0
- package/types/tsconfig.tsbuildinfo +1 -0
- package/.travis.yml +0 -11
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: 'github-actions'
|
|
4
|
+
directory: '/'
|
|
5
|
+
schedule:
|
|
6
|
+
interval: 'daily'
|
|
7
|
+
commit-message:
|
|
8
|
+
prefix: 'chore'
|
|
9
|
+
include: 'scope'
|
|
10
|
+
cooldown:
|
|
11
|
+
default-days: 5
|
|
12
|
+
- package-ecosystem: 'npm'
|
|
13
|
+
directory: '/'
|
|
14
|
+
schedule:
|
|
15
|
+
interval: 'daily'
|
|
16
|
+
commit-message:
|
|
17
|
+
prefix: 'chore'
|
|
18
|
+
include: 'scope'
|
|
19
|
+
cooldown:
|
|
20
|
+
default-days: 5
|
|
21
|
+
- package-ecosystem: 'gomod'
|
|
22
|
+
directory: '/go'
|
|
23
|
+
schedule:
|
|
24
|
+
interval: 'daily'
|
|
25
|
+
commit-message:
|
|
26
|
+
prefix: 'chore'
|
|
27
|
+
include: 'scope'
|
|
28
|
+
cooldown:
|
|
29
|
+
default-days: 5
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Go CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
test:
|
|
6
|
+
strategy:
|
|
7
|
+
fail-fast: false
|
|
8
|
+
matrix:
|
|
9
|
+
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
defaults:
|
|
12
|
+
run:
|
|
13
|
+
working-directory: go
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout Repository
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
- name: Setup Go
|
|
18
|
+
uses: actions/setup-go@v6
|
|
19
|
+
with:
|
|
20
|
+
go-version: '1.25'
|
|
21
|
+
cache-dependency-path: go/go.mod
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: go test -v ./...
|
|
24
|
+
- name: Vet
|
|
25
|
+
run: go vet ./...
|
|
26
|
+
- name: Check formatting
|
|
27
|
+
if: runner.os != 'Windows'
|
|
28
|
+
run: |
|
|
29
|
+
test -z "$(gofmt -l .)" || (gofmt -d . && exit 1)
|
|
30
|
+
|
|
31
|
+
staticcheck:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
defaults:
|
|
34
|
+
run:
|
|
35
|
+
working-directory: go
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout Repository
|
|
38
|
+
uses: actions/checkout@v6
|
|
39
|
+
- name: Setup Go
|
|
40
|
+
uses: actions/setup-go@v6
|
|
41
|
+
with:
|
|
42
|
+
go-version: '1.25'
|
|
43
|
+
cache-dependency-path: go/go.mod
|
|
44
|
+
- name: Install staticcheck
|
|
45
|
+
run: go install honnef.co/go/tools/cmd/staticcheck@latest
|
|
46
|
+
- name: Run staticcheck
|
|
47
|
+
run: staticcheck ./...
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Test & Maybe Release
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
test:
|
|
6
|
+
strategy:
|
|
7
|
+
fail-fast: false
|
|
8
|
+
matrix:
|
|
9
|
+
node: [lts/*, current]
|
|
10
|
+
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout Repository
|
|
14
|
+
uses: actions/checkout@v6
|
|
15
|
+
- name: Use Node.js ${{ matrix.node }}
|
|
16
|
+
uses: actions/setup-node@v6
|
|
17
|
+
with:
|
|
18
|
+
node-version: ${{ matrix.node }}
|
|
19
|
+
- name: Install Dependencies
|
|
20
|
+
run: npm install --no-progress
|
|
21
|
+
- name: Check build is up to date
|
|
22
|
+
run: |
|
|
23
|
+
npm run build
|
|
24
|
+
git diff --exit-code || (echo "::error::Build artifacts not committed. Run 'npm run build' and commit the changes." && exit 1)
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: npm test
|
|
27
|
+
|
|
28
|
+
release:
|
|
29
|
+
name: Release
|
|
30
|
+
needs: [test]
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
issues: write
|
|
36
|
+
pull-requests: write
|
|
37
|
+
id-token: write
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@v6
|
|
41
|
+
with:
|
|
42
|
+
fetch-depth: 0
|
|
43
|
+
- name: Setup Node.js
|
|
44
|
+
uses: actions/setup-node@v6
|
|
45
|
+
with:
|
|
46
|
+
node-version: lts/*
|
|
47
|
+
registry-url: 'https://registry.npmjs.org'
|
|
48
|
+
- name: Install dependencies
|
|
49
|
+
run: npm install --no-progress --no-package-lock --no-save
|
|
50
|
+
- name: Build
|
|
51
|
+
run: npm run build
|
|
52
|
+
- name: Release
|
|
53
|
+
env:
|
|
54
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
+
NPM_CONFIG_PROVENANCE: true
|
|
56
|
+
run: npx semantic-release
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## [1.2.0](https://github.com/rvagg/bit-sequence/compare/v1.1.0...v1.2.0) (2026-02-16)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* dep & CI updates & minor modernisation ([d37d229](https://github.com/rvagg/bit-sequence/commit/d37d229fea152b35e0329af0304bf9abb502abd7))
|
|
6
|
+
|
|
7
|
+
### Trivial Changes
|
|
8
|
+
|
|
9
|
+
* **deps:** bump actions/setup-go from 5 to 6 ([#3](https://github.com/rvagg/bit-sequence/issues/3)) ([8786d6d](https://github.com/rvagg/bit-sequence/commit/8786d6d92408d6ffe069eae6b5cd8bb00c4d2919))
|
package/README.md
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
# bit-sequence
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://nodei.co/npm/bit-sequence/)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Turn an arbitrary sequence of bits from a byte array into an integer**
|
|
6
|
+
|
|
7
|
+
* [JavaScript](#javascript)
|
|
8
|
+
* [Example](#example)
|
|
9
|
+
* [API](#api)
|
|
10
|
+
* [Go](#go)
|
|
11
|
+
* [License and Copyright](#license-and-copyright)
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
Node.js >= 20
|
|
16
|
+
|
|
17
|
+
## JavaScript
|
|
6
18
|
|
|
7
19
|
Given an `Array`-like containing bytes (unsigned 8-bit integers), extract an arbitrary sequence of the underlying bits and convert them into an unsigned integer value.
|
|
8
20
|
|
|
9
21
|
Useful for cases where a sub-sequence of bits within a longer byte sequence is used to form an index, such as in a [hash array mapped trie](https://en.wikipedia.org/wiki/Hash_array_mapped_trie) where an index at each level of the tree structure is formed by incremental chunks of a key's hash.
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
### Example
|
|
12
24
|
|
|
13
25
|
```js
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
import bitSequence from 'bit-sequence'
|
|
27
|
+
import assert from 'node:assert'
|
|
16
28
|
const bytes = new Uint8Array([ 0b00010101, 0b10101000, 0b00000000, 0b00000000 ])
|
|
17
29
|
// extract bits from here ^ to here ^
|
|
18
30
|
const int = bitSequence(bytes, 7, 11)
|
|
19
31
|
assert.strictEqual(int, 0b11010100000) // or `1696`
|
|
20
32
|
```
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
### API
|
|
23
35
|
|
|
24
36
|
**`bitSequence(bytes, start, length)`**
|
|
25
37
|
|
|
@@ -33,6 +45,21 @@ As per the example above, the assumption is that we are extracting bytes where t
|
|
|
33
45
|
|
|
34
46
|
JavaScript's crazy numbers allows us to extract potentially very large bit sequences, but the usual caveats apply at 32-bits and beyond [`Number.MAX_SAFE_INTEGER`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
|
|
35
47
|
|
|
48
|
+
## Go
|
|
49
|
+
|
|
50
|
+
`github.com/rvagg/bit-sequence/go`
|
|
51
|
+
|
|
52
|
+
### API
|
|
53
|
+
|
|
54
|
+
Exports `BitSequence(bytes []byte, bitStart uint32, bitLength uint32) (uint32, error)` where:
|
|
55
|
+
|
|
56
|
+
* `bytes` is a simple byte array of arbitrary length
|
|
57
|
+
* `bitStart` is a _bit_ index to start extraction (not a _byte_ index).
|
|
58
|
+
* `bitLength` is the number of bits to extract from the `bytes` array. This value can only be a maximum of `32`, higher values will be adjusted down.
|
|
59
|
+
|
|
60
|
+
Returns an unsigned integer version of the bit sequence. The most significant bit is not interpreted for a two's compliment representation.
|
|
61
|
+
Returns an error if `bitLength` exceeds 32 or the requested sequence overflows the bytes boundary.
|
|
62
|
+
|
|
36
63
|
## License and Copyright
|
|
37
64
|
|
|
38
65
|
Copyright 2019 Rod Vagg
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package bitsequence
|
|
2
|
+
|
|
3
|
+
import "fmt"
|
|
4
|
+
|
|
5
|
+
// BitSequence turns an arbitrary sequence of bits from a byte array into an integer.
|
|
6
|
+
//
|
|
7
|
+
// - `bytes` is a simple byte array of arbitrary length
|
|
8
|
+
//
|
|
9
|
+
// - `bitStart` is a _bit_ index to start extraction (not a _byte_ index).
|
|
10
|
+
//
|
|
11
|
+
// - `bitLength` is the number of bits to extract from the `bytes` array. This value can only be a maximum of `32`, higher values will be adjusted down.
|
|
12
|
+
//
|
|
13
|
+
// Returns an unsigned integer version of the bit sequence. The most significant bit is not interpreted for a two's compliment representation.
|
|
14
|
+
// Returns an error if `bitLength` exceeds 32 or the requested sequence overflows the bytes boundary.
|
|
15
|
+
func BitSequence(bytes []byte, bitStart uint32, bitLength uint32) (uint32, error) {
|
|
16
|
+
if bitLength > 32 {
|
|
17
|
+
return 0, fmt.Errorf("maximum bits that can be read is 32")
|
|
18
|
+
}
|
|
19
|
+
startOffset := bitStart % 8
|
|
20
|
+
byteCount := (7 + startOffset + bitLength) / 8
|
|
21
|
+
byteStart := bitStart / 8
|
|
22
|
+
endOffset := byteCount*8 - bitLength - startOffset
|
|
23
|
+
|
|
24
|
+
if int(byteStart+byteCount) > len(bytes) {
|
|
25
|
+
return 0, fmt.Errorf("cannot read past end of bytes array")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var result uint32
|
|
29
|
+
|
|
30
|
+
var i uint32
|
|
31
|
+
for i = 0; i < byteCount; i++ {
|
|
32
|
+
local := bytes[byteStart+i]
|
|
33
|
+
var shift uint32
|
|
34
|
+
var localBitLength uint32 = 8 // how many bits of this byte we are extracting
|
|
35
|
+
|
|
36
|
+
if i == 0 {
|
|
37
|
+
localBitLength -= startOffset
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if i == byteCount-1 {
|
|
41
|
+
localBitLength -= endOffset
|
|
42
|
+
shift = endOffset
|
|
43
|
+
local = local >> shift // take off the trailing bits
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if localBitLength < 8 {
|
|
47
|
+
mask := uint8((1 << localBitLength) - 1)
|
|
48
|
+
local = local & mask // mask off the leading bits
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if shift < 8 {
|
|
52
|
+
result = result << (8 - shift)
|
|
53
|
+
}
|
|
54
|
+
result = result | uint32(local)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result, nil
|
|
58
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
package bitsequence
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bufio"
|
|
5
|
+
"encoding/hex"
|
|
6
|
+
"os"
|
|
7
|
+
"strconv"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const FixturesFile = "../test-fixture.csv"
|
|
13
|
+
|
|
14
|
+
type TestCase struct {
|
|
15
|
+
bytes []byte
|
|
16
|
+
start, length, expected uint32
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func TestFixtures(t *testing.T) {
|
|
20
|
+
testCases, err := readTestCases(FixturesFile)
|
|
21
|
+
if err != nil {
|
|
22
|
+
panic(err)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for _, testCase := range testCases {
|
|
26
|
+
actual, err := BitSequence(testCase.bytes, testCase.start, testCase.length)
|
|
27
|
+
if err != nil {
|
|
28
|
+
t.Errorf("Bytes [%s] start=%d length=%d returned an error reading",
|
|
29
|
+
hex.EncodeToString(testCase.bytes),
|
|
30
|
+
testCase.start,
|
|
31
|
+
testCase.length,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
if actual != testCase.expected {
|
|
35
|
+
t.Errorf("Bytes [%s] start=%d length=%d expected %d but got %d",
|
|
36
|
+
hex.EncodeToString(testCase.bytes),
|
|
37
|
+
testCase.start,
|
|
38
|
+
testCase.length,
|
|
39
|
+
testCase.expected,
|
|
40
|
+
actual)
|
|
41
|
+
} /* else {
|
|
42
|
+
fmt.Printf("Bytes [%s] start=%d length=%d expected %d and got %d\n",
|
|
43
|
+
hex.EncodeToString(testCase.bytes),
|
|
44
|
+
testCase.start,
|
|
45
|
+
testCase.length,
|
|
46
|
+
testCase.expected,
|
|
47
|
+
actual)
|
|
48
|
+
} */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func TestReadPastEndErrors(t *testing.T) {
|
|
53
|
+
bytes, err := hex.DecodeString("ff")
|
|
54
|
+
if err != nil {
|
|
55
|
+
panic(err)
|
|
56
|
+
}
|
|
57
|
+
_, err = BitSequence(bytes, 16, 1)
|
|
58
|
+
if err == nil {
|
|
59
|
+
t.Errorf("Should not have allowed reading past end of byte array but did")
|
|
60
|
+
}
|
|
61
|
+
_, err = BitSequence(bytes, 14, 6)
|
|
62
|
+
if err == nil {
|
|
63
|
+
t.Errorf("Should not have allowed reading past end of byte array but did")
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func readTestCases(path string) ([]TestCase, error) {
|
|
68
|
+
file, err := os.Open(path)
|
|
69
|
+
if err != nil {
|
|
70
|
+
return nil, err
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
defer file.Close()
|
|
74
|
+
|
|
75
|
+
scanner := bufio.NewScanner(file)
|
|
76
|
+
scanner.Split(bufio.ScanLines)
|
|
77
|
+
|
|
78
|
+
var testCases []TestCase
|
|
79
|
+
for scanner.Scan() {
|
|
80
|
+
line := scanner.Text()
|
|
81
|
+
s := strings.Split(line, ",")
|
|
82
|
+
|
|
83
|
+
testCase := TestCase{}
|
|
84
|
+
var err error
|
|
85
|
+
var ii int
|
|
86
|
+
|
|
87
|
+
testCase.bytes, err = hex.DecodeString(s[0])
|
|
88
|
+
if err != nil {
|
|
89
|
+
return nil, err
|
|
90
|
+
}
|
|
91
|
+
ii, err = strconv.Atoi(s[1])
|
|
92
|
+
if err != nil {
|
|
93
|
+
return nil, err
|
|
94
|
+
}
|
|
95
|
+
testCase.start = uint32(ii)
|
|
96
|
+
ii, err = strconv.Atoi(s[2])
|
|
97
|
+
if err != nil {
|
|
98
|
+
return nil, err
|
|
99
|
+
}
|
|
100
|
+
testCase.length = uint32(ii)
|
|
101
|
+
ii, err = strconv.Atoi(s[3])
|
|
102
|
+
if err != nil {
|
|
103
|
+
return nil, err
|
|
104
|
+
}
|
|
105
|
+
testCase.expected = uint32(ii)
|
|
106
|
+
|
|
107
|
+
testCases = append(testCases, testCase)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return testCases, nil
|
|
111
|
+
}
|
package/go/go.mod
ADDED
package/js/bit-sequence.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @param {Uint8Array} bytes
|
|
3
|
+
* @param {number} bitStart
|
|
4
|
+
* @param {number} bitLength
|
|
5
|
+
* @returns {number}
|
|
6
|
+
*/
|
|
7
|
+
export default function bitSequence (bytes, bitStart, bitLength) {
|
|
2
8
|
// making an assumption that bytes is an Array-like that will give us one
|
|
3
9
|
// byte per element, so either an Array full of 8-bit integers or a
|
|
4
10
|
// Uint8Array or a Node.js Buffer, or something like that
|
|
@@ -48,5 +54,3 @@ function bitSequence (bytes, bitStart, bitLength) {
|
|
|
48
54
|
|
|
49
55
|
return result
|
|
50
56
|
}
|
|
51
|
-
|
|
52
|
-
module.exports = bitSequence
|
package/js/test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import bitSequence from './bit-sequence.js'
|
|
3
4
|
|
|
4
5
|
function binaryStringToBytes (s) {
|
|
5
6
|
const byteLength = Math.ceil(s.length / 8)
|
|
@@ -11,82 +12,92 @@ function binaryStringToBytes (s) {
|
|
|
11
12
|
return bytes
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
;[
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
].forEach((s) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
assert.strictEqual(bytesHex.replace(/^0+/, ''), parseInt(binary, 2).toString(16))
|
|
15
|
+
test('binaryStringToBytes sanity check', () => {
|
|
16
|
+
;[
|
|
17
|
+
'00000001:01',
|
|
18
|
+
'10000000:80',
|
|
19
|
+
'11111111:ff',
|
|
20
|
+
'11000000:c0',
|
|
21
|
+
'11110000:f0',
|
|
22
|
+
'1111111111111111:ffff',
|
|
23
|
+
'0000000000000001:0001',
|
|
24
|
+
'000000000000000000000001:000001',
|
|
25
|
+
'111111111111111111111111:ffffff',
|
|
26
|
+
'100000001000000010000000:808080',
|
|
27
|
+
'10000000100000001000000010000000:80808080',
|
|
28
|
+
'10000000111111111000000011111111:80ff80ff',
|
|
29
|
+
'001:01',
|
|
30
|
+
'111:07',
|
|
31
|
+
'1111:0f',
|
|
32
|
+
'01111:0f',
|
|
33
|
+
'001111:0f',
|
|
34
|
+
'0000001111:000f',
|
|
35
|
+
'10000000000001111:01000f'
|
|
36
|
+
].forEach((s) => {
|
|
37
|
+
const [binary, hex] = s.split(':')
|
|
38
|
+
const bytesHex = Array.prototype.map.call(
|
|
39
|
+
binaryStringToBytes(binary),
|
|
40
|
+
(b) => b.toString(16).padStart(2, '0')
|
|
41
|
+
).join('')
|
|
42
|
+
assert.strictEqual(bytesHex, hex)
|
|
43
|
+
assert.strictEqual(bytesHex.replace(/^0+/, ''), parseInt(binary, 2).toString(16))
|
|
44
|
+
})
|
|
45
45
|
})
|
|
46
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
|
-
|
|
47
|
+
test('bitSequence extracts correct bit sequences', () => {
|
|
48
|
+
const testCases = [
|
|
49
|
+
'00000001',
|
|
50
|
+
'11111111',
|
|
51
|
+
'01010101',
|
|
52
|
+
'10001000',
|
|
53
|
+
'0000000000000001',
|
|
54
|
+
'0000000100000001',
|
|
55
|
+
'1111111111111111',
|
|
56
|
+
'1010101010101010',
|
|
57
|
+
'0101010101010101',
|
|
58
|
+
'1001001001001001',
|
|
59
|
+
'0100100100100100',
|
|
60
|
+
'1000100010001000',
|
|
61
|
+
'0100010001000100',
|
|
62
|
+
'1111111100000000',
|
|
63
|
+
'0000000011111111',
|
|
64
|
+
'0000111111110000',
|
|
65
|
+
'000000000000000000000001',
|
|
66
|
+
'111111111111111111111111',
|
|
67
|
+
'001001100100110010011111',
|
|
68
|
+
'00000000000000000000000000000001',
|
|
69
|
+
'11111111111111111111111111111111',
|
|
70
|
+
'10000000000000000000000000000001',
|
|
71
|
+
'10001100010110011110001010111010',
|
|
72
|
+
'1111111111111111111111111111111111111111111111111111111111111111',
|
|
73
|
+
'0000000000000000000000000000000000000000000000000000000000000001',
|
|
74
|
+
'1000000000000000000000000000000000000000000000000000000000000001',
|
|
75
|
+
'1010110100111110101110101001010001000000001101011101110010011111',
|
|
76
|
+
'11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
|
|
77
|
+
'1111101000010110000011111100000101110000010110111001100000000000',
|
|
78
|
+
'10010101000110001000101011111111101000010110000011111100000101110000010110111001111000011001000000001000111101010010101001110000'
|
|
79
|
+
]
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
let asserts = 0
|
|
82
|
+
let tooBig = 0
|
|
83
|
+
testCases.forEach((s) => {
|
|
84
|
+
const bytes = binaryStringToBytes(s)
|
|
85
|
+
|
|
86
|
+
for (let start = 0; start < bytes.length * 8; start++) {
|
|
87
|
+
for (let length = 1; length <= bytes.length * 8 - start; length++) {
|
|
88
|
+
const expected = parseInt(s.substring(start, start + length), 2)
|
|
89
|
+
const actual = bitSequence(bytes, start, length)
|
|
90
|
+
if (actual !== expected && actual > Number.MAX_SAFE_INTEGER) {
|
|
91
|
+
tooBig++
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
asserts++
|
|
95
|
+
assert.strictEqual(actual, expected, `[${s}] start=${start} length=${length} ${actual} <> ${expected}`)
|
|
96
|
+
assert.ok(actual >= 0, 'sanity check that we\'re only dealing with unsigned integers')
|
|
97
|
+
}
|
|
88
98
|
}
|
|
89
|
-
}
|
|
90
|
-
})
|
|
99
|
+
})
|
|
91
100
|
|
|
92
|
-
assert.ok(asserts > 10000, 'did a lot of asserts')
|
|
101
|
+
assert.ok(asserts > 10000, 'did a lot of asserts')
|
|
102
|
+
assert.ok(tooBig < 200, 'not too many non-matches beyond MAX_SAFE_INTEGER')
|
|
103
|
+
})
|