synta 0.1.6

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# 15. `example_integer_advanced.py` — Integer edge cases and bigint

[← Example index](index.md) · [example_integer_advanced.py on Codeberg](https://codeberg.org/abbra/synta/src/branch/main/examples/example_integer_advanced.py)

Bindings: `Integer(int)`, `Integer.from_bytes`, `Integer.from_u64`,
`Integer.to_int`, `Integer.to_i128`, `Integer.to_bytes`,
`Encoder.encode_integer` (bigint path), `Encoder.encode_integer_object`.

- Construct integers via all three constructors.
- Show `to_int()` succeeds for small values; raises `OverflowError` for 20-byte serials.
- Use `to_i128()` for up to 16-byte values (i128 max = 2¹²⁷−1); show `OverflowError` beyond that.
- Use `to_bytes()` for arbitrary-precision big-endian representation.
- Encode a 20-byte certificate serial number via `encode_integer(large_python_int)`.
- Verify `encode_integer_object` round-trip.

## Source

```python
#!/usr/bin/env python3
"""
Example 14: Integer edge cases and bigint.

Demonstrates: Integer(int), Integer.from_bytes, Integer.from_u64,
Integer.to_int, Integer.to_i128, Integer.to_bytes,
Encoder.encode_integer (bigint path), Encoder.encode_integer_object.
"""

import synta


def section(title):
    print(f"\n{'─' * 60}\n{title}\n{'─' * 60}")


def demo_constructors():
    section("Integer — three constructors")
    # Constructor 1: Integer(int)
    i1 = synta.Integer(42)
    print(f"  Integer(42).to_int() = {i1.to_int()}")
    assert i1.to_int() == 42

    # Constructor 2: Integer.from_u64(value)
    i2 = synta.Integer.from_u64(2**32)
    print(f"  Integer.from_u64(2**32).to_bytes() = {i2.to_bytes().hex()}")
    assert i2.to_bytes() == (2**32).to_bytes(5, "big", signed=True)

    # Constructor 3: Integer.from_bytes(big_endian_signed_bytes)
    # Use a large 20-byte positive integer (2^159 - 1)
    serial_bytes = b'\x7f' + b'\xff' * 19
    i3 = synta.Integer.from_bytes(serial_bytes)
    print(f"  Integer.from_bytes(20-byte): to_bytes={i3.to_bytes().hex()[:20]}...")


def demo_to_int_overflow():
    section("to_int() — succeeds for small values, OverflowError for big ones")
    small = synta.Integer(2**62)
    print(f"  Integer(2**62).to_int() = {small.to_int()}")

    # A 20-byte serial number overflows i64
    big_bytes = b'\x7f' + b'\xff' * 19  # 20 bytes, fits i128 but not i64
    big = synta.Integer.from_bytes(big_bytes)
    try:
        big.to_int()
        print("  No OverflowError (unexpected)")
    except OverflowError as e:
        print(f"  OverflowError from to_int(): {e}")


def demo_to_i128():
    section("to_i128() — works for 17–16 byte values")
    # 17-byte signed value fits in i128
    val = 2**127 - 1  # i128 max
    val_bytes = val.to_bytes(16, "big", signed=True)  # 16 bytes, fits i128
    i = synta.Integer.from_bytes(val_bytes)
    print(f"  16-byte value (i128 max = 2^127-1): {i.to_i128()}")
    assert i.to_i128() == val

    # Negative value via two's complement
    neg = synta.Integer(-1)
    print(f"  Integer(-1).to_i128() = {neg.to_i128()}")
    assert neg.to_i128() == -1

    # Too large for i128: 17 bytes of 0x80 + 16 bytes 0x00 → > i128 max
    too_big = b'\x00\x80' + b'\x00' * 16  # 18 bytes signed
    big = synta.Integer.from_bytes(too_big)
    try:
        big.to_i128()
        print("  No OverflowError (unexpected)")
    except OverflowError as e:
        print(f"  OverflowError from to_i128(): {e}")


def demo_to_bytes():
    section("to_bytes() — big-endian signed representation")
    # Positive value
    i = synta.Integer(255)
    b = i.to_bytes()
    print(f"  Integer(255).to_bytes() = {b.hex()!r}  (0x00ff — sign byte preserved)")

    # Negative value uses two's complement
    neg = synta.Integer(-1)
    b = neg.to_bytes()
    print(f"  Integer(-1).to_bytes() = {b.hex()!r}  (0xff)")

    # Large value
    large = synta.Integer.from_bytes(b'\x7f' + b'\xff' * 19)
    b = large.to_bytes()
    print(f"  20-byte serial: {b.hex()[:20]}... ({len(b)} bytes)")


def demo_encode_integer_large():
    section("encode_integer — arbitrary-magnitude Python int (bigint path)")
    # Normal small int (i64 path)
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer(42)
    print(f"  encode_integer(42) → {enc.finish().hex()}")

    # 20-byte certificate serial (beyond i64)
    serial = 2**159 - 1  # 0x7ffff...fff, 20 bytes
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer(serial)
    out = enc.finish()
    print(f"  encode_integer(2**159-1): tag=0x{out[0]:02x} length={out[1]} value={out[2:4].hex()}...")
    assert out[0] == 0x02  # INTEGER tag
    assert out[1] == 20    # 20-byte content

    # Round-trip via decode
    dec = synta.Decoder(out, synta.Encoding.DER)
    decoded = dec.decode_integer()
    assert decoded.to_bytes() == b'\x7f' + b'\xff' * 19
    print("  Decode round-trip: OK")

    # Value needing 0x00 sign prefix (high bit set, positive)
    val = 2**127  # bit_length=128, needs 17 bytes signed
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer(val)
    out2 = enc.finish()
    assert out2[2] == 0x00  # leading sign byte
    print(f"  encode_integer(2**127) → content starts 0x{out2[2]:02x}0x{out2[3]:02x} (sign prefix + high byte)")

    # Negative values use two's complement
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer(-128)
    assert enc.finish() == b'\x02\x01\x80'
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer(-129)
    assert enc.finish() == b'\x02\x02\xff\x7f'
    print("  Negative values (two's complement): OK")


def demo_encode_integer_object():
    section("encode_integer_object — encode an Integer instance")
    big = synta.Integer.from_bytes(b'\x01' + b'\x00' * 16)  # 2**128
    enc = synta.Encoder(synta.Encoding.DER)
    enc.encode_integer_object(big)
    data = enc.finish()

    dec = synta.Decoder(data, synta.Encoding.DER)
    decoded = dec.decode_integer()
    assert decoded.to_bytes() == big.to_bytes()
    print(f"  encode_integer_object round-trip: {decoded.to_bytes().hex()}")
    print("  encode_integer_object: OK")


def main():
    print("=" * 60)
    print("Example 14: Integer edge cases and bigint")
    print("=" * 60)
    demo_constructors()
    demo_to_int_overflow()
    demo_to_i128()
    demo_to_bytes()
    demo_encode_integer_large()
    demo_encode_integer_object()
    print("\nAll Integer advanced examples completed.")


if __name__ == "__main__":
    main()
```