sltcore 0.2.1__tar.gz → 0.2.3__tar.gz
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.
- sltcore-0.2.3/.github/workflows/publish_to_pypi.yml +43 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.github/workflows/publish_to_testpypi.yml +1 -3
- sltcore-0.2.3/PKG-INFO +151 -0
- sltcore-0.2.3/README.md +136 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/pyproject.toml +1 -1
- sltcore-0.2.3/src/sltcore/__init__.py +4 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/src/sltcore/types.py +68 -89
- {sltcore-0.2.1 → sltcore-0.2.3}/uv.lock +1 -1
- sltcore-0.2.1/.github/workflows/publish_to_pypi.yml +0 -25
- sltcore-0.2.1/PKG-INFO +0 -106
- sltcore-0.2.1/README.md +0 -91
- sltcore-0.2.1/src/sltcore/__init__.py +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.github/FUNDING.yml +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.gitignore +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.python-version +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.vscode/launch.json +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/.vscode/settings.json +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/LICENSE +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/bump_major.ps1 +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/bump_minor.ps1 +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/bump_patch.ps1 +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/src/sltcore/bits.py +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/tests/test_bits.py +0 -0
- {sltcore-0.2.1 → sltcore-0.2.3}/tests/test_types.py +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build-and-publish:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Extract version from pyproject.toml
|
|
17
|
+
id: version
|
|
18
|
+
run: |
|
|
19
|
+
VERSION=$(grep -m1 '^version = "' pyproject.toml | sed -E 's/^version = "([^"]+)"$/\1/')
|
|
20
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
21
|
+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v3
|
|
25
|
+
|
|
26
|
+
- name: Build distribution
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Publish to PyPI
|
|
30
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
31
|
+
with:
|
|
32
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
33
|
+
packages-dir: dist
|
|
34
|
+
|
|
35
|
+
- name: Create GitHub release and upload artifacts
|
|
36
|
+
uses: softprops/action-gh-release@v2
|
|
37
|
+
with:
|
|
38
|
+
tag_name: ${{ steps.version.outputs.tag }}
|
|
39
|
+
name: ${{ steps.version.outputs.tag }}
|
|
40
|
+
generate_release_notes: true
|
|
41
|
+
files: |
|
|
42
|
+
dist/*.tar.gz
|
|
43
|
+
dist/*.whl
|
|
@@ -2,8 +2,6 @@ name: Publish to TestPyPI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
|
-
release:
|
|
6
|
-
types: [published]
|
|
7
5
|
|
|
8
6
|
jobs:
|
|
9
7
|
build-and-publish:
|
|
@@ -23,4 +21,4 @@ jobs:
|
|
|
23
21
|
with:
|
|
24
22
|
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
|
25
23
|
repository-url: https://test.pypi.org/legacy/
|
|
26
|
-
|
|
24
|
+
packages-dir: dist
|
sltcore-0.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sltcore
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: A toolkit for handling structured data layouts and bit-level operations.
|
|
5
|
+
Project-URL: Homepage, https://github.com/fangface-hub/StructLayoutToolkitCore
|
|
6
|
+
Project-URL: Documentation, https://readthedocs.org
|
|
7
|
+
Project-URL: Repository, https://github.com/fangface-hub/StructLayoutToolkitCore
|
|
8
|
+
Project-URL: Issues, https://github.com/fangface-hub/StructLayoutToolkitCore/issues
|
|
9
|
+
Author: fangface
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: bit,byte,data,deserialization,layout,serialization,struct
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# StructLayoutToolkitCore
|
|
17
|
+
|
|
18
|
+
stlcore is a minimal, high‑cohesion core library for bit‑accurate binary slicing. It provides the fundamental primitives (bits_get, bits_set, InfoSize) required to build struct layout analyzers, binary editors, protocol inspectors, and low‑level testing tools. Designed for clarity, correctness, and composability.
|
|
19
|
+
|
|
20
|
+
## What You Can Do After Installing sltcore
|
|
21
|
+
|
|
22
|
+
sltcore provides a minimal and consistent bit-level abstraction layer for building higher‑level tools such as binary editors, protocol analyzers, and structured layout systems.
|
|
23
|
+
|
|
24
|
+
After installing the package, you can:
|
|
25
|
+
|
|
26
|
+
- Extract arbitrary bit ranges from a bytearray using bits_get
|
|
27
|
+
- Write arbitrary bit ranges into a bytearray using bits_set
|
|
28
|
+
- Represent sizes and offsets uniformly with InfoSize
|
|
29
|
+
- Build structured binary layouts on top of a stable bit‑operation core
|
|
30
|
+
|
|
31
|
+
## Quick Examples: bits_get / bits_set
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
|
|
35
|
+
from sltcore import InfoSize, Info , bits_get, bits_set
|
|
36
|
+
|
|
37
|
+
buf = bytearray(b"\x12\x34\x56\x78")
|
|
38
|
+
|
|
39
|
+
# Extract 4 bits at offset (1 byte, 0 bits)
|
|
40
|
+
value = bits_get(buf, InfoSize(1, 0), InfoSize(0, 4))
|
|
41
|
+
print(value.to_hex) # 0x3
|
|
42
|
+
|
|
43
|
+
# Write 5 bits at offset (2 bytes, 3 bits)
|
|
44
|
+
bits_set(buf, InfoSize(2, 3), InfoSize(0, 5), 0b10101)
|
|
45
|
+
print(buf.hex()) # 12345578
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## sltcore.InfoSize
|
|
50
|
+
|
|
51
|
+
InfoSize is a type that represents offsets and sizes in a unified way.
|
|
52
|
+
It supports addition and subtraction, and stores both byte and bit components.
|
|
53
|
+
|
|
54
|
+
It is used for both offset and size, allowing consistent structural layout definitions.
|
|
55
|
+
|
|
56
|
+
- __Field List__
|
|
57
|
+
|
|
58
|
+
| Field | Type | Description |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| byte | int | the byte size |
|
|
61
|
+
| bit | int | the bit size (0–7)<br> always normalized so that bit < 8 |
|
|
62
|
+
|
|
63
|
+
- __Property List__
|
|
64
|
+
|
|
65
|
+
| Property | Type | Description |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| bits | int | Total bit length.<br> This property converts the stored byte and bit components into a single bit count using the formula byte * 8 + bit |
|
|
68
|
+
| bytes | int | Total byte length.<br> This property converts the combined bit length (byte * 8 + bit) into a byte count using integer division (bits // 8) |
|
|
69
|
+
| hex_digits | int | Number of hexadecimal digits needed to represent the field.<br>Since one hex digit corresponds to 4 bits, the value is calculated as ceil(bits / 4) |
|
|
70
|
+
| mask | int | Bitmask for the field size.<br>It produces a mask that covers exactly bits bits, using the formula (1 << bits) - 1.<br>This mask is typically used for extracting or normalizing values. |
|
|
71
|
+
|
|
72
|
+
## sltcore.Info
|
|
73
|
+
|
|
74
|
+
Info stores an InfoSize and a raw integer value.
|
|
75
|
+
|
|
76
|
+
This allows higher‑level code to treat extracted fields as structured units rather than plain integers.
|
|
77
|
+
|
|
78
|
+
- __Field List__
|
|
79
|
+
|
|
80
|
+
| Field | Type | Description |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| raw_value | int | The internal integer representation of the field.<br>It is obtained by converting the corresponding bytes into an integer and applying the size mask so that the value fits within the bit width specified by InfoSize. |
|
|
83
|
+
| info_size | InfoSize | Size descriptor for this value.<br>It provides the byte and bit length of the field and determines how the raw_value is masked, extracted, and written back into the byte sequence. |
|
|
84
|
+
|
|
85
|
+
- __Classmethod List__
|
|
86
|
+
|
|
87
|
+
| Classmethod | Description |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| from_int | Creates an Info instance from an integer value, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
90
|
+
| from_bytes | Creates an Info instance from a bytes object, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
91
|
+
| from_bytearray | Creates an Info instance from a bytearray, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
92
|
+
| from_float | Creates an Info instance from a float value, converting it to its raw bit representation based on the specified size. |
|
|
93
|
+
|
|
94
|
+
Example: Creating an Info from a float value
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
size = InfoSize(byte=0, bit=32) # 32-bit float
|
|
98
|
+
info = Info.from_float(3.14, size)
|
|
99
|
+
|
|
100
|
+
print(info.raw_value) # IEEE754 raw bits (e.g., 0x4048F5C3)
|
|
101
|
+
print(info.info_size.bits) # 32
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- __Property List__
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| to_unsigned_int | int | Returns the extracted bits as an unsigned integer. |
|
|
109
|
+
| to_signed_int | int | Returns the extracted bits as a signed integer. |
|
|
110
|
+
| to_hex | str | Returns the extracted bits as a hexadecimal string. |
|
|
111
|
+
| to_bytes | bytes | Returns the extracted bits as a bytes object. |
|
|
112
|
+
| to_bool | bool | Returns the extracted bits as a boolean value. |
|
|
113
|
+
| to_float | float | Returns the extracted bits as a float. |
|
|
114
|
+
| byte_swap | Info | Returns a new Info instance with the byte order reversed. |
|
|
115
|
+
|
|
116
|
+
## sltcore.bits_get
|
|
117
|
+
|
|
118
|
+
bits_get extracts a field from a bytearray using an offset and size, and returns an Info object.
|
|
119
|
+
|
|
120
|
+
Input:
|
|
121
|
+
|
|
122
|
+
- bytearray buffer
|
|
123
|
+
- InfoSize offset
|
|
124
|
+
- InfoSize size
|
|
125
|
+
|
|
126
|
+
Output:
|
|
127
|
+
|
|
128
|
+
- Info containing the extracted value and its size
|
|
129
|
+
|
|
130
|
+
## sltcore.bits_set
|
|
131
|
+
|
|
132
|
+
bits_set writes a value into a bytearray at the specified offset.
|
|
133
|
+
|
|
134
|
+
Input:
|
|
135
|
+
|
|
136
|
+
- bytearray buffer
|
|
137
|
+
- InfoSize offset
|
|
138
|
+
- Info value (which includes its size)
|
|
139
|
+
|
|
140
|
+
The function masks and replaces the target bit range inside the buffer.
|
|
141
|
+
|
|
142
|
+
## Documentation
|
|
143
|
+
|
|
144
|
+
For detailed usage, examples, and parameter descriptions, refer to the docstrings via:
|
|
145
|
+
|
|
146
|
+
python
|
|
147
|
+
|
|
148
|
+
- help(sltcore.InfoSize)
|
|
149
|
+
- help(sltcore.Info)
|
|
150
|
+
- help(sltcore.bits_get)
|
|
151
|
+
- help(sltcore.bits_set)
|
sltcore-0.2.3/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# StructLayoutToolkitCore
|
|
2
|
+
|
|
3
|
+
stlcore is a minimal, high‑cohesion core library for bit‑accurate binary slicing. It provides the fundamental primitives (bits_get, bits_set, InfoSize) required to build struct layout analyzers, binary editors, protocol inspectors, and low‑level testing tools. Designed for clarity, correctness, and composability.
|
|
4
|
+
|
|
5
|
+
## What You Can Do After Installing sltcore
|
|
6
|
+
|
|
7
|
+
sltcore provides a minimal and consistent bit-level abstraction layer for building higher‑level tools such as binary editors, protocol analyzers, and structured layout systems.
|
|
8
|
+
|
|
9
|
+
After installing the package, you can:
|
|
10
|
+
|
|
11
|
+
- Extract arbitrary bit ranges from a bytearray using bits_get
|
|
12
|
+
- Write arbitrary bit ranges into a bytearray using bits_set
|
|
13
|
+
- Represent sizes and offsets uniformly with InfoSize
|
|
14
|
+
- Build structured binary layouts on top of a stable bit‑operation core
|
|
15
|
+
|
|
16
|
+
## Quick Examples: bits_get / bits_set
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
|
|
20
|
+
from sltcore import InfoSize, Info , bits_get, bits_set
|
|
21
|
+
|
|
22
|
+
buf = bytearray(b"\x12\x34\x56\x78")
|
|
23
|
+
|
|
24
|
+
# Extract 4 bits at offset (1 byte, 0 bits)
|
|
25
|
+
value = bits_get(buf, InfoSize(1, 0), InfoSize(0, 4))
|
|
26
|
+
print(value.to_hex) # 0x3
|
|
27
|
+
|
|
28
|
+
# Write 5 bits at offset (2 bytes, 3 bits)
|
|
29
|
+
bits_set(buf, InfoSize(2, 3), InfoSize(0, 5), 0b10101)
|
|
30
|
+
print(buf.hex()) # 12345578
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## sltcore.InfoSize
|
|
35
|
+
|
|
36
|
+
InfoSize is a type that represents offsets and sizes in a unified way.
|
|
37
|
+
It supports addition and subtraction, and stores both byte and bit components.
|
|
38
|
+
|
|
39
|
+
It is used for both offset and size, allowing consistent structural layout definitions.
|
|
40
|
+
|
|
41
|
+
- __Field List__
|
|
42
|
+
|
|
43
|
+
| Field | Type | Description |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| byte | int | the byte size |
|
|
46
|
+
| bit | int | the bit size (0–7)<br> always normalized so that bit < 8 |
|
|
47
|
+
|
|
48
|
+
- __Property List__
|
|
49
|
+
|
|
50
|
+
| Property | Type | Description |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| bits | int | Total bit length.<br> This property converts the stored byte and bit components into a single bit count using the formula byte * 8 + bit |
|
|
53
|
+
| bytes | int | Total byte length.<br> This property converts the combined bit length (byte * 8 + bit) into a byte count using integer division (bits // 8) |
|
|
54
|
+
| hex_digits | int | Number of hexadecimal digits needed to represent the field.<br>Since one hex digit corresponds to 4 bits, the value is calculated as ceil(bits / 4) |
|
|
55
|
+
| mask | int | Bitmask for the field size.<br>It produces a mask that covers exactly bits bits, using the formula (1 << bits) - 1.<br>This mask is typically used for extracting or normalizing values. |
|
|
56
|
+
|
|
57
|
+
## sltcore.Info
|
|
58
|
+
|
|
59
|
+
Info stores an InfoSize and a raw integer value.
|
|
60
|
+
|
|
61
|
+
This allows higher‑level code to treat extracted fields as structured units rather than plain integers.
|
|
62
|
+
|
|
63
|
+
- __Field List__
|
|
64
|
+
|
|
65
|
+
| Field | Type | Description |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| raw_value | int | The internal integer representation of the field.<br>It is obtained by converting the corresponding bytes into an integer and applying the size mask so that the value fits within the bit width specified by InfoSize. |
|
|
68
|
+
| info_size | InfoSize | Size descriptor for this value.<br>It provides the byte and bit length of the field and determines how the raw_value is masked, extracted, and written back into the byte sequence. |
|
|
69
|
+
|
|
70
|
+
- __Classmethod List__
|
|
71
|
+
|
|
72
|
+
| Classmethod | Description |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| from_int | Creates an Info instance from an integer value, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
75
|
+
| from_bytes | Creates an Info instance from a bytes object, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
76
|
+
| from_bytearray | Creates an Info instance from a bytearray, ensuring that the value fits within the specified size by applying a bitmask. |
|
|
77
|
+
| from_float | Creates an Info instance from a float value, converting it to its raw bit representation based on the specified size. |
|
|
78
|
+
|
|
79
|
+
Example: Creating an Info from a float value
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
size = InfoSize(byte=0, bit=32) # 32-bit float
|
|
83
|
+
info = Info.from_float(3.14, size)
|
|
84
|
+
|
|
85
|
+
print(info.raw_value) # IEEE754 raw bits (e.g., 0x4048F5C3)
|
|
86
|
+
print(info.info_size.bits) # 32
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- __Property List__
|
|
90
|
+
|
|
91
|
+
| Property | Type | Description |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| to_unsigned_int | int | Returns the extracted bits as an unsigned integer. |
|
|
94
|
+
| to_signed_int | int | Returns the extracted bits as a signed integer. |
|
|
95
|
+
| to_hex | str | Returns the extracted bits as a hexadecimal string. |
|
|
96
|
+
| to_bytes | bytes | Returns the extracted bits as a bytes object. |
|
|
97
|
+
| to_bool | bool | Returns the extracted bits as a boolean value. |
|
|
98
|
+
| to_float | float | Returns the extracted bits as a float. |
|
|
99
|
+
| byte_swap | Info | Returns a new Info instance with the byte order reversed. |
|
|
100
|
+
|
|
101
|
+
## sltcore.bits_get
|
|
102
|
+
|
|
103
|
+
bits_get extracts a field from a bytearray using an offset and size, and returns an Info object.
|
|
104
|
+
|
|
105
|
+
Input:
|
|
106
|
+
|
|
107
|
+
- bytearray buffer
|
|
108
|
+
- InfoSize offset
|
|
109
|
+
- InfoSize size
|
|
110
|
+
|
|
111
|
+
Output:
|
|
112
|
+
|
|
113
|
+
- Info containing the extracted value and its size
|
|
114
|
+
|
|
115
|
+
## sltcore.bits_set
|
|
116
|
+
|
|
117
|
+
bits_set writes a value into a bytearray at the specified offset.
|
|
118
|
+
|
|
119
|
+
Input:
|
|
120
|
+
|
|
121
|
+
- bytearray buffer
|
|
122
|
+
- InfoSize offset
|
|
123
|
+
- Info value (which includes its size)
|
|
124
|
+
|
|
125
|
+
The function masks and replaces the target bit range inside the buffer.
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
For detailed usage, examples, and parameter descriptions, refer to the docstrings via:
|
|
130
|
+
|
|
131
|
+
python
|
|
132
|
+
|
|
133
|
+
- help(sltcore.InfoSize)
|
|
134
|
+
- help(sltcore.Info)
|
|
135
|
+
- help(sltcore.bits_get)
|
|
136
|
+
- help(sltcore.bits_set)
|
|
@@ -1,37 +1,52 @@
|
|
|
1
1
|
"""Type definitions for structured data layouts and bit-level operations."""
|
|
2
2
|
import math
|
|
3
|
-
|
|
3
|
+
import struct
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _has_struct_half():
|
|
8
|
+
"""Checks if the current Python environment supports
|
|
9
|
+
half-precision (16-bit) floats in the struct module."""
|
|
10
|
+
try:
|
|
11
|
+
struct.pack(">e", 1.0)
|
|
12
|
+
return True
|
|
13
|
+
except struct.error:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
HAS_STRUCT_HALF = _has_struct_half() # ← 一度だけ実行
|
|
4
18
|
|
|
5
19
|
|
|
6
20
|
@dataclass(frozen=True)
|
|
7
21
|
class InfoSize:
|
|
8
22
|
"""Represents a size in bytes and bits."""
|
|
9
|
-
byte: int = 0
|
|
10
|
-
bit: int = 0
|
|
23
|
+
byte: int = field(default=0, metadata={"desc": "byte size"})
|
|
24
|
+
bit: int = field(default=0, metadata={"desc": "bit size"})
|
|
11
25
|
|
|
12
26
|
def __post_init__(self):
|
|
13
27
|
"""Ensures that the bit count is normalized to be less than 8,
|
|
14
28
|
and adjusts the byte count accordingly."""
|
|
15
|
-
extra_bytes
|
|
29
|
+
extra_bytes = self.bit >> 3
|
|
30
|
+
new_bits = self.bit & 7
|
|
16
31
|
object.__setattr__(self, "byte", self.byte + extra_bytes)
|
|
17
32
|
object.__setattr__(self, "bit", new_bits)
|
|
18
33
|
|
|
19
34
|
@property
|
|
20
35
|
def bits(self) -> int:
|
|
21
36
|
"""Returns the total size in bits."""
|
|
22
|
-
return self.byte
|
|
37
|
+
return ((self.byte << 3) | self.bit)
|
|
23
38
|
|
|
24
39
|
@property
|
|
25
40
|
def bytes(self) -> int:
|
|
26
41
|
"""Returns the total size in bytes,
|
|
27
42
|
rounding up if there are leftover bits."""
|
|
28
|
-
return (self.bits + 7)
|
|
43
|
+
return ((self.bits + 7) >> 3) # round up
|
|
29
44
|
|
|
30
45
|
@property
|
|
31
46
|
def hex_digits(self) -> int:
|
|
32
47
|
"""Returns the number of hexadecimal digits
|
|
33
48
|
needed to represent the size in bits."""
|
|
34
|
-
return self.bits
|
|
49
|
+
return (self.bits >> 2)
|
|
35
50
|
|
|
36
51
|
@property
|
|
37
52
|
def mask(self) -> int:
|
|
@@ -59,8 +74,9 @@ class InfoSize:
|
|
|
59
74
|
class Info:
|
|
60
75
|
"""Represents a slice of bits extracted from a bytearray,
|
|
61
76
|
along with its size information."""
|
|
62
|
-
raw_value: int
|
|
63
|
-
info_size: InfoSize
|
|
77
|
+
raw_value: int = field(default=0, metadata={"desc": "raw integer value"})
|
|
78
|
+
info_size: InfoSize = field(default_factory=InfoSize,
|
|
79
|
+
metadata={"desc": "size information"})
|
|
64
80
|
|
|
65
81
|
@classmethod
|
|
66
82
|
def from_int(cls, value: int, size: InfoSize):
|
|
@@ -135,65 +151,52 @@ def _float_from_bits(raw: int, bits: int) -> float:
|
|
|
135
151
|
to a Python float, based on the specified bit size.
|
|
136
152
|
Supports 16, 32, and 64 bits.
|
|
137
153
|
"""
|
|
138
|
-
if bits ==
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
f
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return (-1)**s * 0.0
|
|
147
|
-
return (-1)**s * (f / 2**10) * 2**(-14)
|
|
148
|
-
elif e == 31:
|
|
149
|
-
if f == 0:
|
|
150
|
-
return float('inf') if s == 0 else float('-inf')
|
|
151
|
-
return float('nan')
|
|
152
|
-
else:
|
|
153
|
-
return (-1)**s * (1 + f / 2**10) * 2**(e - 15)
|
|
154
|
-
elif bits == 32:
|
|
155
|
-
# IEEE754 single precision
|
|
156
|
-
# High-speed version without using struct
|
|
157
|
-
s = (raw >> 31) & 0x1
|
|
158
|
-
|
|
159
|
-
e = (raw >> 23) & 0xFF
|
|
160
|
-
f = raw & 0x7FFFFF
|
|
161
|
-
|
|
162
|
-
if e == 0:
|
|
163
|
-
return (-1)**s * (f / 2**23) * 2**(-126)
|
|
164
|
-
elif e == 255:
|
|
165
|
-
if f == 0:
|
|
166
|
-
return float('-inf') if s else float('inf')
|
|
167
|
-
return float('nan')
|
|
168
|
-
else:
|
|
169
|
-
return (-1)**s * (1 + f / 2**23) * 2**(e - 127)
|
|
170
|
-
|
|
171
|
-
elif bits == 64:
|
|
172
|
-
# IEEE754 double precision
|
|
173
|
-
s = (raw >> 63) & 0x1
|
|
174
|
-
e = (raw >> 52) & 0x7FF
|
|
175
|
-
f = raw & 0xFFFFFFFFFFFFF
|
|
176
|
-
|
|
177
|
-
if e == 0:
|
|
178
|
-
return (-1)**s * (f / 2**52) * 2**(-1022)
|
|
179
|
-
elif e == 2047:
|
|
180
|
-
if f == 0:
|
|
181
|
-
return float('-inf') if s else float('inf')
|
|
182
|
-
return float('nan')
|
|
183
|
-
else:
|
|
184
|
-
return (-1)**s * (1 + f / 2**52) * 2**(e - 1023)
|
|
185
|
-
|
|
186
|
-
else:
|
|
187
|
-
raise ValueError(''.join([
|
|
188
|
-
f"Unsupported bit size for float conversion: {bits}. ",
|
|
154
|
+
if bits == 64:
|
|
155
|
+
return struct.unpack('>d', struct.pack('>Q', raw))[0]
|
|
156
|
+
if bits == 32:
|
|
157
|
+
# Use struct to unpack the integer as a float
|
|
158
|
+
return struct.unpack('>f', struct.pack('>I', raw))[0]
|
|
159
|
+
if bits != 16:
|
|
160
|
+
raise ValueError("".join([
|
|
161
|
+
f"Unsupported float bit size: {bits}. ",
|
|
189
162
|
"Only 16, 32, and 64 are supported."
|
|
190
163
|
]))
|
|
164
|
+
if HAS_STRUCT_HALF:
|
|
165
|
+
# Use struct to unpack the integer as a half-precision float
|
|
166
|
+
return struct.unpack('>e', struct.pack('>H', raw))[0]
|
|
167
|
+
# IEEE754 half-precision (16bit) → float32
|
|
168
|
+
s = (raw >> 15) & 0x0001
|
|
169
|
+
e = (raw >> 10) & 0x001F
|
|
170
|
+
f = raw & 0x03FF
|
|
171
|
+
|
|
172
|
+
if e == 0:
|
|
173
|
+
if f == 0:
|
|
174
|
+
return (-1)**s * 0.0
|
|
175
|
+
return (-1)**s * (f / 2**10) * 2**(-14)
|
|
176
|
+
if e == 31:
|
|
177
|
+
if f == 0:
|
|
178
|
+
return float('inf') if s == 0 else float('-inf')
|
|
179
|
+
return float('nan')
|
|
180
|
+
return (-1)**s * (1 + f / 2**10) * 2**(e - 15)
|
|
191
181
|
|
|
192
182
|
|
|
193
183
|
def _float_to_bits(value: float, bits: int) -> int:
|
|
194
184
|
"""Converts a Python float to its raw integer representation
|
|
195
185
|
based on the specified bit size. Supports 16, 32, and 64 bits.
|
|
196
186
|
"""
|
|
187
|
+
if bits == 64:
|
|
188
|
+
return struct.unpack('>Q', struct.pack('>d', value))[0]
|
|
189
|
+
if bits == 32:
|
|
190
|
+
# Use struct to pack the float into bytes and then unpack as an integer
|
|
191
|
+
return struct.unpack('>I', struct.pack('>f', value))[0]
|
|
192
|
+
if bits != 16:
|
|
193
|
+
raise ValueError("".join([
|
|
194
|
+
f"Unsupported float bit size: {bits}. ",
|
|
195
|
+
"Only 16, 32, and 64 are supported."
|
|
196
|
+
]))
|
|
197
|
+
if HAS_STRUCT_HALF:
|
|
198
|
+
# Use struct to pack the float into bytes and then unpack as an integer
|
|
199
|
+
return struct.unpack('>H', struct.pack('>e', value))[0]
|
|
197
200
|
# Determine the sign and absolute value
|
|
198
201
|
# Use math.copysign to correctly detect negative zero
|
|
199
202
|
sign = 1 if math.copysign(1.0, value) < 0 else 0
|
|
@@ -205,40 +208,16 @@ def _float_to_bits(value: float, bits: int) -> int:
|
|
|
205
208
|
|
|
206
209
|
# NaN / Inf
|
|
207
210
|
if math.isnan(value):
|
|
208
|
-
|
|
209
|
-
return 0x7E00
|
|
210
|
-
if bits == 32:
|
|
211
|
-
return 0x7FC00000
|
|
212
|
-
if bits == 64:
|
|
213
|
-
return 0x7FF8000000000000
|
|
211
|
+
return 0x7E00
|
|
214
212
|
|
|
215
213
|
# Handle infinity
|
|
216
214
|
if math.isinf(value):
|
|
217
|
-
|
|
218
|
-
return (sign << 15) | 0x7C00
|
|
219
|
-
if bits == 32:
|
|
220
|
-
return (sign << 31) | 0x7F800000
|
|
221
|
-
if bits == 64:
|
|
222
|
-
return (sign << 63) | 0x7FF0000000000000
|
|
215
|
+
return (sign << 15) | 0x7C00
|
|
223
216
|
|
|
224
217
|
# Compute exponent and mantissa
|
|
225
218
|
e = int(math.floor(math.log(v, 2)))
|
|
226
219
|
mant = v / (2**e) - 1.0
|
|
227
220
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return (sign << 15) | (exp << 10) | frac
|
|
232
|
-
|
|
233
|
-
elif bits == 32:
|
|
234
|
-
exp = e + 127
|
|
235
|
-
frac = int(mant * (2**23))
|
|
236
|
-
return (sign << 31) | (exp << 23) | frac
|
|
237
|
-
|
|
238
|
-
elif bits == 64:
|
|
239
|
-
exp = e + 1023
|
|
240
|
-
frac = int(mant * (2**52))
|
|
241
|
-
return (sign << 63) | (exp << 52) | frac
|
|
242
|
-
|
|
243
|
-
else:
|
|
244
|
-
raise ValueError(f"Unsupported float bit size: {bits}")
|
|
221
|
+
exp = e + 15
|
|
222
|
+
frac = int(mant * (2**10))
|
|
223
|
+
return (sign << 15) | (exp << 10) | frac
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
name: Publish to PyPI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
release:
|
|
6
|
-
types: [published]
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build-and-publish:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v4
|
|
14
|
-
|
|
15
|
-
- name: Install uv
|
|
16
|
-
uses: astral-sh/setup-uv@v3
|
|
17
|
-
|
|
18
|
-
- name: Build wheel
|
|
19
|
-
run: uv build
|
|
20
|
-
|
|
21
|
-
- name: Publish to PyPI
|
|
22
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
23
|
-
with:
|
|
24
|
-
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
25
|
-
packages_dir: dist
|
sltcore-0.2.1/PKG-INFO
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: sltcore
|
|
3
|
-
Version: 0.2.1
|
|
4
|
-
Summary: A toolkit for handling structured data layouts and bit-level operations.
|
|
5
|
-
Project-URL: Homepage, https://github.com/fangface-hub/StructLayoutToolkitCore
|
|
6
|
-
Project-URL: Documentation, https://readthedocs.org
|
|
7
|
-
Project-URL: Repository, https://github.com/fangface-hub/StructLayoutToolkitCore
|
|
8
|
-
Project-URL: Issues, https://github.com/fangface-hub/StructLayoutToolkitCore/issues
|
|
9
|
-
Author: fangface
|
|
10
|
-
License-Expression: MIT
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Keywords: bit,byte,data,deserialization,layout,serialization,struct
|
|
13
|
-
Requires-Python: >=3.12
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
|
|
16
|
-
# StructLayoutToolkitCore
|
|
17
|
-
|
|
18
|
-
stlcore is a minimal, high‑cohesion core library for bit‑accurate binary slicing. It provides the fundamental primitives (bits_get, bits_set, InfoSize) required to build struct layout analyzers, binary editors, protocol inspectors, and low‑level testing tools. Designed for clarity, correctness, and composability.
|
|
19
|
-
|
|
20
|
-
## What You Can Do After Installing sltcore
|
|
21
|
-
|
|
22
|
-
sltcore provides a minimal and consistent bit-level abstraction layer for building higher‑level tools such as binary editors, protocol analyzers, and structured layout systems.
|
|
23
|
-
|
|
24
|
-
After installing the package, you can:
|
|
25
|
-
|
|
26
|
-
- Extract arbitrary bit ranges from a bytearray using bits_get
|
|
27
|
-
- Write arbitrary bit ranges into a bytearray using bits_set
|
|
28
|
-
- Represent sizes and offsets uniformly with InfoSize
|
|
29
|
-
- Build structured binary layouts on top of a stable bit‑operation core
|
|
30
|
-
|
|
31
|
-
## Quick Examples: bits_get / bits_set
|
|
32
|
-
|
|
33
|
-
```python
|
|
34
|
-
|
|
35
|
-
from sltcore.types import InfoSize, Info
|
|
36
|
-
from sltcore.bits import bits_get, bits_set
|
|
37
|
-
|
|
38
|
-
buf = bytearray(b"\x12\x34\x56\x78")
|
|
39
|
-
|
|
40
|
-
# Extract 4 bits at offset (1 byte, 0 bits)
|
|
41
|
-
value = bits_get(buf, InfoSize(1, 0), InfoSize(0, 4))
|
|
42
|
-
print(value.to_hex) # 0x3
|
|
43
|
-
|
|
44
|
-
# Write 5 bits at offset (2 bytes, 3 bits)
|
|
45
|
-
bits_set(buf, InfoSize(2, 3), InfoSize(0, 5), 0b10101)
|
|
46
|
-
print(buf.hex()) # 12345578
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## sltcore.type.InfoSize
|
|
51
|
-
|
|
52
|
-
InfoSize is a type that represents offsets and sizes in a unified way.
|
|
53
|
-
It supports addition and subtraction, and stores both byte and bit components.
|
|
54
|
-
|
|
55
|
-
- byte: the byte component
|
|
56
|
-
- bit: the bit component (0–7)
|
|
57
|
-
|
|
58
|
-
always normalized so that bit < 8
|
|
59
|
-
|
|
60
|
-
It is used for both offset and size, allowing consistent structural layout definitions.
|
|
61
|
-
|
|
62
|
-
## sltcore.type.Info
|
|
63
|
-
|
|
64
|
-
Info stores an InfoSize and a raw integer value.
|
|
65
|
-
|
|
66
|
-
- The value returned by bits_get is an Info instance
|
|
67
|
-
- The value passed to bits_set is also an Info instance
|
|
68
|
-
|
|
69
|
-
This allows higher‑level code to treat extracted fields as structured units rather than plain integers.
|
|
70
|
-
|
|
71
|
-
## sltcore.bits.bits_get
|
|
72
|
-
|
|
73
|
-
bits_get extracts a field from a bytearray using an offset and size, and returns an Info object.
|
|
74
|
-
|
|
75
|
-
Input:
|
|
76
|
-
|
|
77
|
-
- bytearray buffer
|
|
78
|
-
- InfoSize offset
|
|
79
|
-
- InfoSize size
|
|
80
|
-
|
|
81
|
-
Output:
|
|
82
|
-
|
|
83
|
-
- Info containing the extracted value and its size
|
|
84
|
-
|
|
85
|
-
## sltcore.bits.bits_set
|
|
86
|
-
|
|
87
|
-
bits_set writes a value into a bytearray at the specified offset.
|
|
88
|
-
|
|
89
|
-
Input:
|
|
90
|
-
|
|
91
|
-
- bytearray buffer
|
|
92
|
-
- InfoSize offset
|
|
93
|
-
- Info value (which includes its size)
|
|
94
|
-
|
|
95
|
-
The function masks and replaces the target bit range inside the buffer.
|
|
96
|
-
|
|
97
|
-
## Documentation
|
|
98
|
-
|
|
99
|
-
For detailed usage, examples, and parameter descriptions, refer to the docstrings via:
|
|
100
|
-
|
|
101
|
-
python
|
|
102
|
-
|
|
103
|
-
- help(sltcore.type.InfoSize)
|
|
104
|
-
- help(sltcore.type.Info)
|
|
105
|
-
- help(sltcore.bits.bits_get)
|
|
106
|
-
- help(sltcore.bits.bits_set)
|
sltcore-0.2.1/README.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# StructLayoutToolkitCore
|
|
2
|
-
|
|
3
|
-
stlcore is a minimal, high‑cohesion core library for bit‑accurate binary slicing. It provides the fundamental primitives (bits_get, bits_set, InfoSize) required to build struct layout analyzers, binary editors, protocol inspectors, and low‑level testing tools. Designed for clarity, correctness, and composability.
|
|
4
|
-
|
|
5
|
-
## What You Can Do After Installing sltcore
|
|
6
|
-
|
|
7
|
-
sltcore provides a minimal and consistent bit-level abstraction layer for building higher‑level tools such as binary editors, protocol analyzers, and structured layout systems.
|
|
8
|
-
|
|
9
|
-
After installing the package, you can:
|
|
10
|
-
|
|
11
|
-
- Extract arbitrary bit ranges from a bytearray using bits_get
|
|
12
|
-
- Write arbitrary bit ranges into a bytearray using bits_set
|
|
13
|
-
- Represent sizes and offsets uniformly with InfoSize
|
|
14
|
-
- Build structured binary layouts on top of a stable bit‑operation core
|
|
15
|
-
|
|
16
|
-
## Quick Examples: bits_get / bits_set
|
|
17
|
-
|
|
18
|
-
```python
|
|
19
|
-
|
|
20
|
-
from sltcore.types import InfoSize, Info
|
|
21
|
-
from sltcore.bits import bits_get, bits_set
|
|
22
|
-
|
|
23
|
-
buf = bytearray(b"\x12\x34\x56\x78")
|
|
24
|
-
|
|
25
|
-
# Extract 4 bits at offset (1 byte, 0 bits)
|
|
26
|
-
value = bits_get(buf, InfoSize(1, 0), InfoSize(0, 4))
|
|
27
|
-
print(value.to_hex) # 0x3
|
|
28
|
-
|
|
29
|
-
# Write 5 bits at offset (2 bytes, 3 bits)
|
|
30
|
-
bits_set(buf, InfoSize(2, 3), InfoSize(0, 5), 0b10101)
|
|
31
|
-
print(buf.hex()) # 12345578
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## sltcore.type.InfoSize
|
|
36
|
-
|
|
37
|
-
InfoSize is a type that represents offsets and sizes in a unified way.
|
|
38
|
-
It supports addition and subtraction, and stores both byte and bit components.
|
|
39
|
-
|
|
40
|
-
- byte: the byte component
|
|
41
|
-
- bit: the bit component (0–7)
|
|
42
|
-
|
|
43
|
-
always normalized so that bit < 8
|
|
44
|
-
|
|
45
|
-
It is used for both offset and size, allowing consistent structural layout definitions.
|
|
46
|
-
|
|
47
|
-
## sltcore.type.Info
|
|
48
|
-
|
|
49
|
-
Info stores an InfoSize and a raw integer value.
|
|
50
|
-
|
|
51
|
-
- The value returned by bits_get is an Info instance
|
|
52
|
-
- The value passed to bits_set is also an Info instance
|
|
53
|
-
|
|
54
|
-
This allows higher‑level code to treat extracted fields as structured units rather than plain integers.
|
|
55
|
-
|
|
56
|
-
## sltcore.bits.bits_get
|
|
57
|
-
|
|
58
|
-
bits_get extracts a field from a bytearray using an offset and size, and returns an Info object.
|
|
59
|
-
|
|
60
|
-
Input:
|
|
61
|
-
|
|
62
|
-
- bytearray buffer
|
|
63
|
-
- InfoSize offset
|
|
64
|
-
- InfoSize size
|
|
65
|
-
|
|
66
|
-
Output:
|
|
67
|
-
|
|
68
|
-
- Info containing the extracted value and its size
|
|
69
|
-
|
|
70
|
-
## sltcore.bits.bits_set
|
|
71
|
-
|
|
72
|
-
bits_set writes a value into a bytearray at the specified offset.
|
|
73
|
-
|
|
74
|
-
Input:
|
|
75
|
-
|
|
76
|
-
- bytearray buffer
|
|
77
|
-
- InfoSize offset
|
|
78
|
-
- Info value (which includes its size)
|
|
79
|
-
|
|
80
|
-
The function masks and replaces the target bit range inside the buffer.
|
|
81
|
-
|
|
82
|
-
## Documentation
|
|
83
|
-
|
|
84
|
-
For detailed usage, examples, and parameter descriptions, refer to the docstrings via:
|
|
85
|
-
|
|
86
|
-
python
|
|
87
|
-
|
|
88
|
-
- help(sltcore.type.InfoSize)
|
|
89
|
-
- help(sltcore.type.Info)
|
|
90
|
-
- help(sltcore.bits.bits_get)
|
|
91
|
-
- help(sltcore.bits.bits_set)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|