Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ The `_compat` module provides automatic feature detection and optimized primitiv
This ensures optimal performance on modern Lua while maintaining compatibility
with older versions.

### Raw Operations (bit32 and bit64)

The bit32 and bit64 modules provide `raw_*` variants for performance-critical code:
- `raw_band`, `raw_bor`, `raw_bxor`, `raw_bnot`
- `raw_lshift`, `raw_rshift`, `raw_arshift`
- `raw_rol`, `raw_ror`
- `raw_add`

These bypass the `to_unsigned()` wrapper used on LuaJIT, returning signed
integers when the high bit is set. On other platforms they behave identically
to regular operations. Use for crypto code and tight loops where the sign
interpretation doesn't matter.

Note: Shift amounts >= 32 (or >= 64 for bit64) have platform-specific behavior
in raw functions. Callers should keep shift amounts in valid range.

## Testing

Tests use Lua table-based vectors for easy maintenance:
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ local xored = bit64.bxor(

Example: `0x123456789ABCDEF0` is represented as `{0x12345678, 0x9ABCDEF0}`

### Raw Operations (Performance-Critical Code)

The `bit32` and `bit64` modules provide `raw_*` variants for performance-critical
code paths like cryptographic operations. These bypass the unsigned conversion
wrapper used on LuaJIT, providing direct access to native bit library functions.

**Available functions (bit32 and bit64):**
- `raw_band`, `raw_bor`, `raw_bxor`, `raw_bnot`
- `raw_lshift`, `raw_rshift`, `raw_arshift`
- `raw_rol`, `raw_ror`
- `raw_add`

**Important:** On LuaJIT, raw_* functions may return **signed** 32-bit integers:

```lua
local bit32 = require("bitn").bit32

-- Regular function (always unsigned)
bit32.bxor(0x80000000, 1) --> 2147483649

-- Raw function (signed on LuaJIT)
bit32.raw_bxor(0x80000000, 1) --> -2147483647 (same bit pattern!)
```

**When to use raw_* functions:**
- Chained bitwise operations (XOR, AND, OR, rotate) where sign doesn't matter
- Crypto algorithms (ChaCha20, etc.) that only care about bit patterns
- Tight loops where the `to_unsigned()` overhead is measurable

**When NOT to use raw_* functions:**
- When comparing results (`<`, `>`, `==`)
- When doing arithmetic on the results
- When formatting/displaying values
- When you need guaranteed unsigned semantics

## Development

### Setup
Expand Down
58 changes: 58 additions & 0 deletions src/bitn/_compat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,33 @@ if ok and result then
return native_band(r, MASK32)
end

-- Raw operations provide direct access to native bit functions without the
-- to_unsigned() wrapper. On Lua 5.3+, these are identical to wrapped versions
-- since native operators already return unsigned values.
-- Shifts must mask to 32 bits since native operators work on 64-bit values.
_compat.raw_band = native_band
_compat.raw_bor = native_bor
_compat.raw_bxor = native_bxor
_compat.raw_bnot = function(a)
return native_band(native_bnot(a), MASK32)
end
_compat.raw_lshift = function(a, n)
if n >= 32 then
return 0
end
return native_band(native_lshift(a, n), MASK32)
end
_compat.raw_rshift = function(a, n)
if n >= 32 then
return 0
end
return native_rshift(native_band(a, MASK32), n)
end
_compat.raw_arshift = _compat.arshift
-- No native rol/ror on Lua 5.3+
_compat.raw_rol = nil
_compat.raw_ror = nil

return _compat
end
end
Expand Down Expand Up @@ -218,6 +245,25 @@ if bit_lib then
end
end

-- Raw operations provide direct access to native bit functions without the
-- to_unsigned() wrapper. On LuaJIT, these return signed 32-bit integers.
-- On Lua 5.2 (bit32 library), these are identical to wrapped versions.
_compat.raw_band = bit_band
_compat.raw_bor = bit_bor
_compat.raw_bxor = bit_bxor
_compat.raw_bnot = bit_bnot
_compat.raw_lshift = bit_lshift
_compat.raw_rshift = bit_rshift
_compat.raw_arshift = bit_arshift
-- rol/ror only available on LuaJIT (bit library), not Lua 5.2 (bit32 library)
if bit_lib.rol then
_compat.raw_rol = bit_lib.rol
_compat.raw_ror = bit_lib.ror
else
_compat.raw_rol = nil
_compat.raw_ror = nil
end

return _compat
end

Expand Down Expand Up @@ -317,4 +363,16 @@ function _compat.arshift(a, n)
return r
end

-- Raw operations for pure Lua fallback are identical to wrapped versions
-- since there's no native library to bypass.
_compat.raw_band = _compat.band
_compat.raw_bor = _compat.bor
_compat.raw_bxor = _compat.bxor
_compat.raw_bnot = _compat.bnot
_compat.raw_lshift = _compat.lshift
_compat.raw_rshift = _compat.rshift
_compat.raw_arshift = _compat.arshift
_compat.raw_rol = nil
_compat.raw_ror = nil

return _compat
5 changes: 2 additions & 3 deletions src/bitn/bit16.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ local _compat = require("bitn._compat")

-- Cache methods as locals for faster access
local compat_band = _compat.band
local compat_bnot = _compat.bnot
local compat_bor = _compat.bor
local compat_bxor = _compat.bxor
local compat_bnot = _compat.bnot
local compat_lshift = _compat.lshift
local compat_rshift = _compat.rshift
local impl_name = _compat.impl_name
local math_floor = math.floor

-- 16-bit mask constant
local MASK16 = 0xFFFF

local math_floor = math.floor

--------------------------------------------------------------------------------
-- Core operations
--------------------------------------------------------------------------------
Expand Down
Loading
Loading