std.bits

Reed's standard library. Imported with use std.bits; not on disk.

Functions

pack_bytesPacks four bytes into an i32, b0 occupying the least significant byte.

pack_bytes #

line 243
func pack_bytes(b0: i32, b1: i32, b2: i32, b3: i32) -> i32

Packs four bytes into an i32, b0 occupying the least significant byte.

Only the low 8 bits of each argument are used, so an out-of-range byte cannot corrupt its neighbours.

A free function rather than a method, because it has no receiver: a method's first parameter is its receiver (spec section 7.1), and none of these four bytes is more the subject of the operation than the others.

Parameters
b0 — the least significant byte
b1 — the second byte
b2 — the third byte
b3 — the most significant byte
See also
i32.byte
Source
func pack_bytes(b0: i32, b1: i32, b2: i32, b3: i32) -> i32 {
  return (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);
}

Methods

i32.mask_lowA mask of the low n bits: n ones in the least significant positions.
i32.bit_rangeExtracts width bits starting at bit offset, as an unsigned value.
i32.set_bit_rangeReplaces width bits at bit offset with the low width bits of value.
i32.bitWhether bit index is set.
i32.with_bitReturns self with bit index set to value's truth.
i32.reverse_bitsReverses the order of all 32 bits.
i32.bswapReverses byte order, converting between little- and big-endian.
i64.bswapReverses byte order of a 64-bit value.
i32.cloCounts leading one bits, the complement of clz.
i32.ctoCounts trailing one bits, the complement of ctz.
i32.parityThe parity of the set bits.
i32.lowest_bitIsolates the lowest set bit, clearing every other bit.
i32.clear_lowest_bitClears the lowest set bit, leaving every other bit.
i32.highest_bitIsolates the highest set bit: the largest power of two not exceeding self.
i32.shl_wideA left shift that is not masked modulo the operand width.
i32.shr_wideAn unsigned right shift that is not masked modulo the operand width.
i32.sign_extendSign-extends the low width bits, treating bit width - 1 as the sign.
i32.byteExtracts byte index as an unsigned value in 0..=255.

i32.mask_low #

line 31
func i32.mask_low(self: i32) -> i32

A mask of the low n bits: n ones in the least significant positions.

mask_low(0) is 0 and mask_low(32) is -1 (all ones). The second case is why this exists as a function: the obvious (1 << n) - 1 gives 0 at n == 32, because WebAssembly masks the shift count to 5 bits and 1 << 32 is 1. Counts above 32 also answer all-ones rather than wrapping around to a small mask.

Parameters
n — how many low bits to set, clamped to 0..=32
Returns
a mask with the low n bits set
See also
i32.bit_range
Source
func i32.mask_low(self: i32) -> i32 {
  if (self <= 0) { return 0; }
  if (self >= 32) { return -1; }
  return (1 << self) - 1;
}

i32.bit_range #

line 48
func i32.bit_range(self: i32, offset: i32, width: i32) -> i32

Extracts width bits starting at bit offset, as an unsigned value.

The extracted field is shifted down to bit 0 and zero-extended, so a 4-bit field of all ones reads as 15, never as -1.

An offset or width outside 0..=32, or a field extending past bit 31, yields 0 rather than trapping or reading adjacent bits.

Parameters
offset — the index of the field's lowest bit
width — how many bits the field occupies
See also
i32.set_bit_range, i32.mask_low
Source
func i32.bit_range(self: i32, offset: i32, width: i32) -> i32 {
  if (offset < 0 || width <= 0 || offset >= 32 || offset + width > 32) { return 0; }
  return (self >>> offset) & width.mask_low();
}

i32.set_bit_range #

line 64
func i32.set_bit_range(self: i32, offset: i32, width: i32, value: i32) -> i32

Replaces width bits at bit offset with the low width bits of value.

Bits of value above width are discarded rather than corrupting neighbouring fields, which is the whole reason to use this over hand-rolled shifting.

An out-of-range offset/width returns self unchanged.

Parameters
offset — the index of the field's lowest bit
width — how many bits the field occupies
value — the new field contents, taken from its low width bits
See also
i32.bit_range
Source
func i32.set_bit_range(self: i32, offset: i32, width: i32, value: i32) -> i32 {
  if (offset < 0 || width <= 0 || offset >= 32 || offset + width > 32) { return self; }
  let mask: i32 = width.mask_low() << offset;
  return (self & ~mask) | ((value << offset) & mask);
}

i32.bit #

line 78
func i32.bit(self: i32, index: i32) -> i32

Whether bit index is set.

An index outside 0..=31 answers 0 rather than consulting a wrapped bit, which is what a bare (self >> index) & 1 would do.

Parameters
index — which bit to test, from 0 (least significant)
Returns
1 when the bit is set, 0 otherwise
See also
i32.with_bit
Source
func i32.bit(self: i32, index: i32) -> i32 {
  if (index < 0 || index > 31) { return 0; }
  return (self >>> index) & 1;
}

i32.with_bit #

line 90
func i32.with_bit(self: i32, index: i32, value: i32) -> i32

Returns self with bit index set to value's truth.

An index outside 0..=31 returns self unchanged.

Parameters
index — which bit to write, from 0 (least significant)
value — zero clears the bit; any nonzero value sets it
See also
i32.bit
Source
func i32.with_bit(self: i32, index: i32, value: i32) -> i32 {
  if (index < 0 || index > 31) { return self; }
  if (value != 0) { return self | (1 << index); }
  return self & ~(1 << index);
}

i32.reverse_bits #

line 103
func i32.reverse_bits(self: i32) -> i32

Reverses the order of all 32 bits.

The standard five-step butterfly: swap adjacent bits, then pairs, then nibbles, then bytes, then halves. Constant time, no loop, no table.

Returns
self with bit i moved to bit 31 - i
See also
i32.bswap
Source
func i32.reverse_bits(self: i32) -> i32 {
  var v: i32 = self;
  v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1);
  v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2);
  v = ((v >>> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
  v = ((v >>> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
  return (v >>> 16) | (v << 16);
}

i32.bswap #

line 118
func i32.bswap(self: i32) -> i32

Reverses byte order, converting between little- and big-endian.

The conversion is its own inverse, so one function serves both directions.

Returns
self with its four bytes in the opposite order
See also
i32.reverse_bits
Source
func i32.bswap(self: i32) -> i32 {
  let v: i32 = ((self >>> 8) & 0x00FF00FF) | ((self & 0x00FF00FF) << 8);
  return (v >>> 16) | (v << 16);
}

i64.bswap #

line 126
func i64.bswap(self: i64) -> i64

Reverses byte order of a 64-bit value.

Returns
self with its eight bytes in the opposite order
Source
func i64.bswap(self: i64) -> i64 {
  var v: i64 = self;
  v = ((v >>> 8) & 0x00FF00FF00FF00FF) | ((v & 0x00FF00FF00FF00FF) << 8);
  v = ((v >>> 16) & 0x0000FFFF0000FFFF) | ((v & 0x0000FFFF0000FFFF) << 16);
  return (v >>> 32) | (v << 32);
}

i32.clo #

line 137
func i32.clo(self: i32) -> i32

Counts leading one bits, the complement of clz.

Returns
the number of ones above the highest zero bit, 0..=32
See also
i32.clz (core)
Source
func i32.clo(self: i32) -> i32 {
  return (~self).clz();
}

i32.cto #

line 145
func i32.cto(self: i32) -> i32

Counts trailing one bits, the complement of ctz.

Returns
the number of ones below the lowest zero bit, 0..=32
See also
i32.ctz (core)
Source
func i32.cto(self: i32) -> i32 {
  return (~self).ctz();
}

i32.parity #

line 153
func i32.parity(self: i32) -> i32

The parity of the set bits.

Returns
1 when an odd number of bits are set, 0 when even
See also
i32.popcnt (core)
Source
func i32.parity(self: i32) -> i32 {
  return self.popcnt() & 1;
}

i32.lowest_bit #

line 164
func i32.lowest_bit(self: i32) -> i32

Isolates the lowest set bit, clearing every other bit.

Returns 0 for an input of zero. This is the classic x & -x, correct at every input including i32 minimum.

Returns
a value with only self's lowest set bit
See also
i32.clear_lowest_bit
Source
func i32.lowest_bit(self: i32) -> i32 {
  return self & (0 - self);
}

i32.clear_lowest_bit #

line 175
func i32.clear_lowest_bit(self: i32) -> i32

Clears the lowest set bit, leaving every other bit.

Returns 0 for an input of zero. Iterating this is the standard way to visit exactly the set bits of a word, one per iteration.

Returns
self with its lowest set bit cleared
See also
i32.lowest_bit
Source
func i32.clear_lowest_bit(self: i32) -> i32 {
  return self & (self - 1);
}

i32.highest_bit #

line 185
func i32.highest_bit(self: i32) -> i32

Isolates the highest set bit: the largest power of two not exceeding self.

Returns 0 for zero. Treats self as unsigned, so a negative input answers the sign bit, -2147483648.

Returns
a value with only self's highest set bit
Source
func i32.highest_bit(self: i32) -> i32 {
  if (self == 0) { return 0; }
  return 1 << (31 - self.clz());
}

i32.shl_wide #

line 198
func i32.shl_wide(self: i32, count: i32) -> i32

A left shift that is not masked modulo the operand width.

1 << 32 is 1 in WebAssembly, because the shift count is masked to five bits. This returns 0 for any count at or above 32, which is what the arithmetic meaning of a shift says it should be. A negative count also yields 0.

Parameters
count — how far to shift
See also
i32.shr_wide
Source
func i32.shl_wide(self: i32, count: i32) -> i32 {
  if (count < 0 || count >= 32) { return 0; }
  return self << count;
}

i32.shr_wide #

line 209
func i32.shr_wide(self: i32, count: i32) -> i32

An unsigned right shift that is not masked modulo the operand width.

Returns 0 for any count at or above 32, rather than >>>'s masked behaviour.

Parameters
count — how far to shift
See also
i32.shl_wide
Source
func i32.shr_wide(self: i32, count: i32) -> i32 {
  if (count < 0 || count >= 32) { return 0; }
  return self >>> count;
}

i32.sign_extend #

line 223
func i32.sign_extend(self: i32, width: i32) -> i32

Sign-extends the low width bits, treating bit width - 1 as the sign.

Turns a value read out of a narrow packed field back into a signed i32: a 4-bit field holding 0b1111 sign-extends to -1 rather than staying 15.

A width outside 1..=32 returns self unchanged.

Parameters
width — how many bits are significant
See also
i32.bit_range
Source
func i32.sign_extend(self: i32, width: i32) -> i32 {
  if (width < 1 || width > 32) { return self; }
  let shift: i32 = 32 - width;
  return (self << shift) >> shift;
}

i32.byte #

line 253
func i32.byte(self: i32, index: i32) -> i32

Extracts byte index as an unsigned value in 0..=255.

An index outside 0..=3 yields 0.

Parameters
index — which byte, 0 being least significant
See also
i32.pack_bytes
Source
func i32.byte(self: i32, index: i32) -> i32 {
  if (index < 0 || index > 3) { return 0; }
  return (self >>> (index * 8)) & 0xFF;
}