Packages
Bitpack is a high-performance bit-level packer for rows/events with small fields. Includes BPX (Binary Payload eXchange) for automatic compression with integrity verification. Perfect for IoT, gaming, telemetry, and any scenario requiring compact binary serialization.
Current section
Files
Jump to
Current section
Files
examples/integration_example.exs
#!/usr/bin/env elixir
# Integration Example: Bitpack + BPX
# This example shows how to use Bitpack for efficient bit-level packing
# combined with BPX for automatic compression selection.
Mix.install([
{:jason, "~> 1.4"}
])
# Add the current project to the code path
Code.prepend_path(Path.join(__DIR__, "../lib"))
# Load the modules
Code.require_file(Path.join(__DIR__, "../lib/bitpack.ex"))
Code.require_file(Path.join(__DIR__, "../lib/bpx.ex"))
defmodule IntegrationExample do
@moduledoc """
Example demonstrating Bitpack + BPX integration for IoT sensor data.
Scenario: IoT sensors collecting temperature, humidity, battery level,
and status flags. We want maximum compression for network transmission.
"""
def run do
IO.puts("=== Bitpack + BPX Integration Example ===\n")
# Define sensor data spec
spec = [
{:timestamp, {:u, 32}}, # Unix timestamp (32 bits)
{:sensor_id, {:u, 16}}, # Sensor ID (16 bits)
{:temperature, {:i, 12}}, # Temperature in 0.1°C, signed (-204.8 to +204.7°C)
{:humidity, {:u, 7}}, # Humidity 0-100% (7 bits)
{:battery, {:u, 8}}, # Battery level 0-255 (8 bits)
{:online, {:bool}}, # Online status (1 bit)
{:alarm, {:bool}}, # Alarm status (1 bit)
{:checksum, {:u, 8}} # Simple checksum (8 bits)
]
# Generate sample sensor data
sensor_data = generate_sensor_data(1000)
IO.puts("Generated #{length(sensor_data)} sensor readings")
IO.puts("Sample reading: #{inspect(Enum.at(sensor_data, 0))}\n")
# Step 1: Pack with Bitpack
IO.puts("Step 1: Packing with Bitpack...")
bitpack_binary = Bitpack.pack(sensor_data, spec)
bitpack_size = byte_size(bitpack_binary)
IO.puts(" Bitpack size: #{format_bytes(bitpack_size)}")
IO.puts(" Bytes per reading: #{Float.round(bitpack_size / length(sensor_data), 2)}")
# Step 2: Compare with JSON
json_binary = Jason.encode!(sensor_data) |> IO.iodata_to_binary()
json_size = byte_size(json_binary)
IO.puts(" JSON size: #{format_bytes(json_size)}")
IO.puts(" Bitpack vs JSON: #{Float.round((1 - bitpack_size / json_size) * 100, 1)}% smaller\n")
# Step 3: Wrap with BPX for additional compression
IO.puts("Step 2: Wrapping with BPX...")
bpx_envelope = BPX.wrap_auto(bitpack_binary, algos: [:deflate, :zstd, :brotli])
bpx_size = byte_size(bpx_envelope)
{:ok, bpx_info} = BPX.inspect_envelope(bpx_envelope)
IO.puts(" BPX algorithm: #{bpx_info.algorithm}")
IO.puts(" BPX envelope size: #{format_bytes(bpx_size)}")
IO.puts(" Additional compression: #{Float.round(bpx_info.compression_ratio * 100, 1)}%")
IO.puts(" Total size reduction vs JSON: #{Float.round((1 - bpx_size / json_size) * 100, 1)}%\n")
# Step 4: Demonstrate round-trip
IO.puts("Step 3: Verifying round-trip integrity...")
{:ok, restored_bitpack, _meta} = BPX.unwrap(bpx_envelope)
restored_data = Bitpack.unpack(restored_bitpack, spec)
if restored_data == sensor_data do
IO.puts(" ✓ Round-trip successful - data integrity verified")
else
IO.puts(" ✗ Round-trip failed - data corruption detected")
end
# Step 5: Performance comparison
IO.puts("\nStep 4: Performance Summary")
IO.puts(" Original JSON: #{format_bytes(json_size)}")
IO.puts(" Bitpack only: #{format_bytes(bitpack_size)} (#{Float.round((1 - bitpack_size / json_size) * 100, 1)}% reduction)")
IO.puts(" Bitpack + BPX: #{format_bytes(bpx_size)} (#{Float.round((1 - bpx_size / json_size) * 100, 1)}% reduction)")
IO.puts(" Compression ratio: #{Float.round(json_size / bpx_size, 1)}:1")
# Step 6: Show network transmission benefits
IO.puts("\nStep 5: Network Transmission Benefits")
readings_per_day = 24 * 60 # Every minute
daily_json = json_size * readings_per_day / length(sensor_data)
daily_bpx = bpx_size * readings_per_day / length(sensor_data)
IO.puts(" Daily data (1440 readings):")
IO.puts(" JSON: #{format_bytes(round(daily_json))}")
IO.puts(" Bitpack+BPX: #{format_bytes(round(daily_bpx))}")
IO.puts(" Daily savings: #{format_bytes(round(daily_json - daily_bpx))}")
IO.puts(" Monthly savings: #{format_bytes(round((daily_json - daily_bpx) * 30))}")
end
defp generate_sensor_data(count) do
base_time = System.system_time(:second)
Enum.map(1..count, fn i ->
temp = :rand.uniform(400) - 200 # -20.0 to +20.0°C in 0.1°C units
humidity = :rand.uniform(101) - 1 # 0-100%
battery = :rand.uniform(256) - 1 # 0-255
reading = %{
timestamp: base_time + i * 60, # Every minute
sensor_id: rem(i - 1, 100) + 1, # Sensor IDs 1-100
temperature: temp,
humidity: humidity,
battery: battery,
online: :rand.uniform() > 0.05, # 95% online
alarm: :rand.uniform() > 0.9 # 10% alarm rate
}
# Add simple checksum
checksum = rem(reading.temperature + reading.humidity + reading.battery, 256)
Map.put(reading, :checksum, checksum)
end)
end
defp format_bytes(bytes) when bytes < 1024, do: "#{bytes}B"
defp format_bytes(bytes) when bytes < 1024 * 1024 do
"#{Float.round(bytes / 1024, 1)}KB"
end
defp format_bytes(bytes) do
"#{Float.round(bytes / (1024 * 1024), 1)}MB"
end
end
# Run the example
IntegrationExample.run()