xmhuffman 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Cotruta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: xmhuffman
3
+ Version: 0.1.0
4
+ Summary: Cython bindings for Microsoft xVelocity/Vertipaq canonical-Huffman string decoding
5
+ Home-page: https://github.com/Hugoberry/xmhuffman-cython
6
+ Author: Igor Cotruta
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 Igor Cotruta
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://github.com/Hugoberry/xmhuffman-cython
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
+ Requires-Python: >=3.8
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Dynamic: home-page
38
+ Dynamic: license-file
39
+ Dynamic: requires-python
40
+
41
+ # xmhuffman
42
+
43
+ [![PyPI version](https://img.shields.io/pypi/v/xmhuffman.svg)](https://pypi.org/project/xmhuffman/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
45
+
46
+ A small, fast Cython extension that decodes the canonical-Huffman string
47
+ dictionary pages used by xVelocity / Vertipaq column stores — the storage
48
+ format inside Power BI `.pbix` files (the `DataModel` part) and Excel
49
+ Power Pivot workbooks (`xl/model/item.data`).
50
+
51
+ Provides a tight C kernel for what would otherwise be a per-symbol Python
52
+ loop. On real `.pbix` files this is **30–50× faster** than an equivalent
53
+ pure-Python implementation and removes a hot path that dominates table
54
+ extraction time in tools like [pbixray](https://github.com/Hugoberry/pbixray).
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install xmhuffman
60
+ ```
61
+
62
+ Building from source requires a C compiler and Cython ≥ 3.0:
63
+
64
+ ```bash
65
+ git clone https://github.com/Hugoberry/xmhuffman-cython
66
+ cd xmhuffman-cython
67
+ pip install -e .
68
+ ```
69
+
70
+ ## Usage
71
+
72
+ The library exposes a tiny surface — one entry point for the common case
73
+ plus a few lower-level helpers.
74
+
75
+ ### Decode a dictionary page
76
+
77
+ ```python
78
+ import xmhuffman
79
+
80
+ # All inputs come straight from the Vertipaq dictionary page metadata:
81
+ # bitstream — compressed_string_buffer (bytes)
82
+ # encode_array_128 — 128-byte nibble-packed code-length array
83
+ # offsets — per-string start bit offsets (sequence of u32)
84
+ # total_bits — store_total_bits (end of last string)
85
+ strings: list[bytes] = xmhuffman.decode_page(
86
+ bitstream,
87
+ encode_array_128,
88
+ offsets,
89
+ total_bits,
90
+ swap=True, # apply the byte-pair swap inside the extension
91
+ )
92
+ ```
93
+
94
+ Output is `list[bytes]`. Charset interpretation is the caller's choice:
95
+ Vertipaq pages flag themselves as either single-charset (latin-1 / ANSI,
96
+ one Python `str` per record) or general (the byte stream is UTF-16LE).
97
+
98
+ ### Lower-level building blocks
99
+
100
+ For callers that want to amortize table construction across pages, or
101
+ just to unit-test pieces:
102
+
103
+ ```python
104
+ # Expand the 128-byte nibble-packed array to 256 plain bytes of lengths.
105
+ lengths = xmhuffman.decompress_encode_array(encode_array_128)
106
+
107
+ # Pair-swap a buffer (bytes 2k and 2k+1 swap; trailing odd byte left as-is).
108
+ swapped = xmhuffman.swap_bytes(raw)
109
+
110
+ # Build the flat decode table once, reuse it across decode calls.
111
+ table_bytes, max_len = xmhuffman.build_table(encode_array_128)
112
+ strings = xmhuffman.decode_with_table(
113
+ bitstream, table_bytes, max_len, offsets, total_bits, swap=True,
114
+ )
115
+ ```
116
+
117
+ ## Format notes
118
+
119
+ The on-disk format is documented publicly in Microsoft's open
120
+ specification [\[MS-XLDM\] §2.7.4 — Huffman
121
+ Compression](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9).
122
+ Each dictionary page is, schematically:
123
+
124
+ | Field | Description |
125
+ |---|---|
126
+ | `encode_array` | 128 bytes, two 4-bit code lengths per byte (low nibble = symbol `2i`, high = `2i+1`). Value 0 means "symbol unused". Per [\[MS-XLDM\]](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9) codeword lengths are between 2 and 15 bits. |
127
+ | `uiDecodeBits` | Width of the on-disk primary lookup table (≤ 12). This decoder uses a single flat `2^max_len` table instead and ignores `uiDecodeBits`. |
128
+ | `compressed_string_buffer` | The bitstream itself, with adjacent bytes pair-swapped on disk. No padding between strings. |
129
+ | `store_total_bits` | Total logical bit length; end sentinel for the last string. |
130
+ | `vector_of_record_handle_structures` | Per-record `(bit_offset, page_id)`; sorted offsets per page give the per-string start boundaries. |
131
+
132
+ Codes are classical Huffman, encoded canonically by sorting
133
+ `(length, symbol)` ascending and incrementing the code with a left-shift
134
+ on length changes — exactly the reconstruction described in
135
+ [\[MS-XLDM\] §2.7.4.1.5](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9).
136
+
137
+ ### Character-set modes
138
+
139
+ [\[MS-XLDM\] §2.7.4.1.4](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9)
140
+ distinguishes two modes per page:
141
+
142
+ - **Single character set** (`character_set_type_identifier = 0x000aba91`)
143
+ — only the low byte of each character is Huffman-encoded; the upper
144
+ (charset) byte is stored once on the page and must be reinserted by
145
+ the caller to recover the original 2-byte character stream.
146
+ - **Multiple character sets** (`0x000aba92`) — both bytes are encoded;
147
+ the output byte stream is consumed directly as UTF-16LE.
148
+
149
+ This decoder emits raw `bytes` either way; reassembly of UTF-16 characters
150
+ (including reinserting the single-charset upper byte) is the caller's
151
+ responsibility.
152
+
153
+ ## Performance
154
+
155
+ Apples-to-apples against an equivalent pure-Python decoder on a few real
156
+ `.pbix` files:
157
+
158
+ | File | Strings | Python ref | xmhuffman | Speedup |
159
+ |---|---:|---:|---:|---:|
160
+ | Adventure Works DW 2020 | 191,489 | 449 ms | 10.0 ms | 45× |
161
+ | Sales & Marketing sample | 103,290 | 160 ms | 5.3 ms | 30× |
162
+ | Retail Analysis sample | 9 | 144 ms | 2.9 ms | 50× |
163
+
164
+ The kernel does one unaligned 64-bit big-endian load, one shift, one
165
+ mask, one table lookup and one byte store per output symbol. The decode
166
+ table is a flat `2^max_len` array of `uint16_t` (≤ 64 KB; usually 1–8 KB)
167
+ that fits comfortably in L1/L2.
168
+
169
+ The GIL is released around the inner work, so callers can decode
170
+ multiple pages or columns from worker threads without contention.
171
+
172
+ ## Project layout
173
+
174
+ ```
175
+ xmhuffman-cython/
176
+ ├── xmhuffman.pyx # Cython surface
177
+ ├── xmhuffman.pxd # C declarations
178
+ ├── src/xmhuffman_kernel.c # C kernel
179
+ ├── include/xmhuffman_kernel.h
180
+ ├── tests/ # correctness tests
181
+ └── bench/ # micro-benchmark
182
+ ```
183
+
184
+ ## Testing
185
+
186
+ ```bash
187
+ pip install -e .
188
+ pip install pytest
189
+ pytest tests/ -v
190
+ ```
191
+
192
+ The basic test suite checks each helper against a pure-Python reference
193
+ implementation. An additional integration test (`tests/test_pbix.py`)
194
+ decodes pages out of real `.pbix` files and asserts byte-identity with
195
+ the reference; it is skipped automatically when fixtures aren't
196
+ available.
197
+
198
+ ## Scope and non-goals
199
+
200
+ - **Not** a general-purpose Huffman library. Alphabets are fixed at 256
201
+ symbols, codeword lengths are capped at 15 bits, and the bitstream
202
+ convention is the one used by Vertipaq pages.
203
+ - **Not** an encoder. Round-tripping pages is out of scope.
204
+ - **No** charset conversion inside the extension. The decoder returns
205
+ raw `bytes`; the caller picks between `latin-1` and paired UTF-16LE
206
+ based on the page's character-set identifier.
207
+
208
+ ## License
209
+
210
+ MIT. See [LICENSE](LICENSE).
211
+
212
+ ## Acknowledgements
213
+
214
+ This package is the third in a family of thin Cython wrappers around
215
+ Microsoft column-store / compression formats, alongside
216
+ [xpress8-python](https://github.com/Hugoberry/xpress8-python) and
217
+ [xpress9-python](https://github.com/Hugoberry/xpress9-python).
218
+
219
+ The format itself is documented publicly in Microsoft's open
220
+ specification [\[MS-XLDM\] — Spreadsheet Data Model File
221
+ Format](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/),
222
+ which the implementation here follows. The Python reference and
223
+ end-to-end test fixtures come from the
224
+ [pbixray](https://github.com/Hugoberry/pbixray) project.
@@ -0,0 +1,184 @@
1
+ # xmhuffman
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/xmhuffman.svg)](https://pypi.org/project/xmhuffman/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ A small, fast Cython extension that decodes the canonical-Huffman string
7
+ dictionary pages used by xVelocity / Vertipaq column stores — the storage
8
+ format inside Power BI `.pbix` files (the `DataModel` part) and Excel
9
+ Power Pivot workbooks (`xl/model/item.data`).
10
+
11
+ Provides a tight C kernel for what would otherwise be a per-symbol Python
12
+ loop. On real `.pbix` files this is **30–50× faster** than an equivalent
13
+ pure-Python implementation and removes a hot path that dominates table
14
+ extraction time in tools like [pbixray](https://github.com/Hugoberry/pbixray).
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install xmhuffman
20
+ ```
21
+
22
+ Building from source requires a C compiler and Cython ≥ 3.0:
23
+
24
+ ```bash
25
+ git clone https://github.com/Hugoberry/xmhuffman-cython
26
+ cd xmhuffman-cython
27
+ pip install -e .
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ The library exposes a tiny surface — one entry point for the common case
33
+ plus a few lower-level helpers.
34
+
35
+ ### Decode a dictionary page
36
+
37
+ ```python
38
+ import xmhuffman
39
+
40
+ # All inputs come straight from the Vertipaq dictionary page metadata:
41
+ # bitstream — compressed_string_buffer (bytes)
42
+ # encode_array_128 — 128-byte nibble-packed code-length array
43
+ # offsets — per-string start bit offsets (sequence of u32)
44
+ # total_bits — store_total_bits (end of last string)
45
+ strings: list[bytes] = xmhuffman.decode_page(
46
+ bitstream,
47
+ encode_array_128,
48
+ offsets,
49
+ total_bits,
50
+ swap=True, # apply the byte-pair swap inside the extension
51
+ )
52
+ ```
53
+
54
+ Output is `list[bytes]`. Charset interpretation is the caller's choice:
55
+ Vertipaq pages flag themselves as either single-charset (latin-1 / ANSI,
56
+ one Python `str` per record) or general (the byte stream is UTF-16LE).
57
+
58
+ ### Lower-level building blocks
59
+
60
+ For callers that want to amortize table construction across pages, or
61
+ just to unit-test pieces:
62
+
63
+ ```python
64
+ # Expand the 128-byte nibble-packed array to 256 plain bytes of lengths.
65
+ lengths = xmhuffman.decompress_encode_array(encode_array_128)
66
+
67
+ # Pair-swap a buffer (bytes 2k and 2k+1 swap; trailing odd byte left as-is).
68
+ swapped = xmhuffman.swap_bytes(raw)
69
+
70
+ # Build the flat decode table once, reuse it across decode calls.
71
+ table_bytes, max_len = xmhuffman.build_table(encode_array_128)
72
+ strings = xmhuffman.decode_with_table(
73
+ bitstream, table_bytes, max_len, offsets, total_bits, swap=True,
74
+ )
75
+ ```
76
+
77
+ ## Format notes
78
+
79
+ The on-disk format is documented publicly in Microsoft's open
80
+ specification [\[MS-XLDM\] §2.7.4 — Huffman
81
+ Compression](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9).
82
+ Each dictionary page is, schematically:
83
+
84
+ | Field | Description |
85
+ |---|---|
86
+ | `encode_array` | 128 bytes, two 4-bit code lengths per byte (low nibble = symbol `2i`, high = `2i+1`). Value 0 means "symbol unused". Per [\[MS-XLDM\]](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9) codeword lengths are between 2 and 15 bits. |
87
+ | `uiDecodeBits` | Width of the on-disk primary lookup table (≤ 12). This decoder uses a single flat `2^max_len` table instead and ignores `uiDecodeBits`. |
88
+ | `compressed_string_buffer` | The bitstream itself, with adjacent bytes pair-swapped on disk. No padding between strings. |
89
+ | `store_total_bits` | Total logical bit length; end sentinel for the last string. |
90
+ | `vector_of_record_handle_structures` | Per-record `(bit_offset, page_id)`; sorted offsets per page give the per-string start boundaries. |
91
+
92
+ Codes are classical Huffman, encoded canonically by sorting
93
+ `(length, symbol)` ascending and incrementing the code with a left-shift
94
+ on length changes — exactly the reconstruction described in
95
+ [\[MS-XLDM\] §2.7.4.1.5](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9).
96
+
97
+ ### Character-set modes
98
+
99
+ [\[MS-XLDM\] §2.7.4.1.4](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/f70b41f2-ca64-44a1-9e6f-53e63f6a5ee9)
100
+ distinguishes two modes per page:
101
+
102
+ - **Single character set** (`character_set_type_identifier = 0x000aba91`)
103
+ — only the low byte of each character is Huffman-encoded; the upper
104
+ (charset) byte is stored once on the page and must be reinserted by
105
+ the caller to recover the original 2-byte character stream.
106
+ - **Multiple character sets** (`0x000aba92`) — both bytes are encoded;
107
+ the output byte stream is consumed directly as UTF-16LE.
108
+
109
+ This decoder emits raw `bytes` either way; reassembly of UTF-16 characters
110
+ (including reinserting the single-charset upper byte) is the caller's
111
+ responsibility.
112
+
113
+ ## Performance
114
+
115
+ Apples-to-apples against an equivalent pure-Python decoder on a few real
116
+ `.pbix` files:
117
+
118
+ | File | Strings | Python ref | xmhuffman | Speedup |
119
+ |---|---:|---:|---:|---:|
120
+ | Adventure Works DW 2020 | 191,489 | 449 ms | 10.0 ms | 45× |
121
+ | Sales & Marketing sample | 103,290 | 160 ms | 5.3 ms | 30× |
122
+ | Retail Analysis sample | 9 | 144 ms | 2.9 ms | 50× |
123
+
124
+ The kernel does one unaligned 64-bit big-endian load, one shift, one
125
+ mask, one table lookup and one byte store per output symbol. The decode
126
+ table is a flat `2^max_len` array of `uint16_t` (≤ 64 KB; usually 1–8 KB)
127
+ that fits comfortably in L1/L2.
128
+
129
+ The GIL is released around the inner work, so callers can decode
130
+ multiple pages or columns from worker threads without contention.
131
+
132
+ ## Project layout
133
+
134
+ ```
135
+ xmhuffman-cython/
136
+ ├── xmhuffman.pyx # Cython surface
137
+ ├── xmhuffman.pxd # C declarations
138
+ ├── src/xmhuffman_kernel.c # C kernel
139
+ ├── include/xmhuffman_kernel.h
140
+ ├── tests/ # correctness tests
141
+ └── bench/ # micro-benchmark
142
+ ```
143
+
144
+ ## Testing
145
+
146
+ ```bash
147
+ pip install -e .
148
+ pip install pytest
149
+ pytest tests/ -v
150
+ ```
151
+
152
+ The basic test suite checks each helper against a pure-Python reference
153
+ implementation. An additional integration test (`tests/test_pbix.py`)
154
+ decodes pages out of real `.pbix` files and asserts byte-identity with
155
+ the reference; it is skipped automatically when fixtures aren't
156
+ available.
157
+
158
+ ## Scope and non-goals
159
+
160
+ - **Not** a general-purpose Huffman library. Alphabets are fixed at 256
161
+ symbols, codeword lengths are capped at 15 bits, and the bitstream
162
+ convention is the one used by Vertipaq pages.
163
+ - **Not** an encoder. Round-tripping pages is out of scope.
164
+ - **No** charset conversion inside the extension. The decoder returns
165
+ raw `bytes`; the caller picks between `latin-1` and paired UTF-16LE
166
+ based on the page's character-set identifier.
167
+
168
+ ## License
169
+
170
+ MIT. See [LICENSE](LICENSE).
171
+
172
+ ## Acknowledgements
173
+
174
+ This package is the third in a family of thin Cython wrappers around
175
+ Microsoft column-store / compression formats, alongside
176
+ [xpress8-python](https://github.com/Hugoberry/xpress8-python) and
177
+ [xpress9-python](https://github.com/Hugoberry/xpress9-python).
178
+
179
+ The format itself is documented publicly in Microsoft's open
180
+ specification [\[MS-XLDM\] — Spreadsheet Data Model File
181
+ Format](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xldm/),
182
+ which the implementation here follows. The Python reference and
183
+ end-to-end test fixtures come from the
184
+ [pbixray](https://github.com/Hugoberry/pbixray) project.
@@ -0,0 +1,52 @@
1
+ #ifndef XMHUFFMAN_KERNEL_H
2
+ #define XMHUFFMAN_KERNEL_H
3
+
4
+ #include <stddef.h>
5
+ #include <stdint.h>
6
+
7
+ #if defined(_MSC_VER)
8
+ #include <BaseTsd.h>
9
+ typedef SSIZE_T xmh_ssize_t;
10
+ #else
11
+ #include <sys/types.h>
12
+ typedef ssize_t xmh_ssize_t;
13
+ #endif
14
+
15
+ #ifdef __cplusplus
16
+ extern "C" {
17
+ #endif
18
+
19
+ #define XMH_MAX_CODE_LEN 15
20
+ #define XMH_TABLE_MAX_SIZE (1u << XMH_MAX_CODE_LEN) /* 32768 u16 entries */
21
+
22
+ /* Expand 128-byte nibble-packed code lengths into 256 plain bytes.
23
+ * Low nibble of byte i is the length for symbol 2i, high nibble for 2i+1. */
24
+ void xmh_decompress_encode_array(const uint8_t *in128, uint8_t *out256);
25
+
26
+ /* Pair-swap: bytes 2k and 2k+1 swap; a trailing odd byte is copied as-is.
27
+ * Safe to alias (in == out). */
28
+ void xmh_swap_pairs(const uint8_t *in_buf, uint8_t *out_buf, size_t n);
29
+
30
+ /* Build the flat canonical-Huffman decode table.
31
+ * lengths256 : 256 code lengths in [0, 15]
32
+ * table : caller-provided u16 buffer, must hold 1 << *out_max_len
33
+ * entries. Safe upper bound: XMH_TABLE_MAX_SIZE.
34
+ * Each entry packs (symbol << 8) | code_len.
35
+ * out_max_len: receives the max non-zero code length (0 if alphabet empty).
36
+ * Returns 0 on success, negative on invalid code (Kraft failure). */
37
+ int xmh_build_table(const uint8_t *lengths256,
38
+ uint16_t *table,
39
+ unsigned *out_max_len);
40
+
41
+ /* Decode one bit-slice [start_bit, end_bit) of a *swapped* bitstream into
42
+ * out[0..out_cap). Returns bytes written, or -1 on overflow. */
43
+ xmh_ssize_t xmh_decode_one(const uint8_t *swapped, size_t swapped_len,
44
+ const uint16_t *table, unsigned max_len,
45
+ uint64_t start_bit, uint64_t end_bit,
46
+ uint8_t *out, size_t out_cap);
47
+
48
+ #ifdef __cplusplus
49
+ }
50
+ #endif
51
+
52
+ #endif /* XMHUFFMAN_KERNEL_H */
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel", "Cython>=3.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "xmhuffman"
7
+ version = "0.1.0"
8
+ description = "Cython bindings for Microsoft xVelocity/Vertipaq canonical-Huffman string decoding"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Igor Cotruta" }]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ "Topic :: Software Development :: Libraries :: Python Modules",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/Hugoberry/xmhuffman-cython"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,35 @@
1
+ import sys
2
+ from setuptools import setup, Extension
3
+ from Cython.Build import cythonize
4
+
5
+ is_macos = sys.platform == "darwin"
6
+ is_windows = sys.platform == "win32"
7
+ is_linux = not is_macos and not is_windows
8
+
9
+ if is_windows:
10
+ extra_compile_args = ["/O2"]
11
+ elif is_linux:
12
+ extra_compile_args = ["-O3", "-fPIC"]
13
+ else:
14
+ extra_compile_args = ["-O3"]
15
+
16
+ xmhuffman_module = Extension(
17
+ "xmhuffman",
18
+ sources=["xmhuffman.pyx", "src/xmhuffman_kernel.c"],
19
+ include_dirs=["include"],
20
+ extra_compile_args=extra_compile_args,
21
+ )
22
+
23
+ setup(
24
+ name="xmhuffman",
25
+ version="0.1.0",
26
+ description="Cython bindings for Microsoft xVelocity/Vertipaq canonical-Huffman string decoding (PBIX/Power Pivot)",
27
+ author="Igor Cotruta",
28
+ url="https://github.com/Hugoberry/xmhuffman-cython",
29
+ ext_modules=cythonize(
30
+ [xmhuffman_module],
31
+ compiler_directives={"language_level": "3"},
32
+ ),
33
+ python_requires=">=3.8",
34
+ zip_safe=False,
35
+ )