Skip to content

Latest commit

 

History

History
195 lines (140 loc) · 5.19 KB

File metadata and controls

195 lines (140 loc) · 5.19 KB

BinaryDataKit

A Swift package for efficient binary data scanning, parsing, and serialization with support for endianness-aware operations and specialized file format handling.

Overview

BinaryDataKit provides type-safe, performant tools for working with binary data in Swift. It includes:

  • DataScanner: Sequential binary data scanning with endianness support
  • IFFScanner: Specialized scanner for Interchange File Format (IFF) containers
  • PackableType Protocol: Easy serialization/deserialization for Swift types
  • Data Extensions: Convenient methods for direct data value extraction

Features

  • ✅ Type-safe binary data scanning with automatic endianness handling
  • ✅ Support for all Swift numeric types (Int8-64, UInt8-64, Float16/32/64)
  • ✅ String scanning with configurable encoding and null-termination
  • ✅ Array scanning for efficient bulk data reading
  • ✅ IFF file format support with chunk ID and size parsing
  • ✅ Protocol-based serialization system
  • ✅ Direct Data extensions for value extraction
  • ✅ Comprehensive error handling
  • ✅ Cross-platform support (iOS 14+, macOS 12+, tvOS 14+, visionOS 1+)

Installation

Swift Package Manager

Add BinaryDataKit to your project by adding the following to your Package.swift:

dependencies: [
    .package(url: "https://github.com/apparata/BinaryDataKit.git", from: "1.0.0")
]

Or add it through Xcode:

  1. File → Add Package Dependencies...
  2. Enter the package URL
  3. Click Add Package

Quick Start

Basic Data Scanning

Scanning moves the scanning position.

import BinaryDataKit

let data = Data([0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0])
let scanner = DataScanner(data: data, endianness: .little)

// Scan individual values
let byte: UInt8 = try scanner.scanUInt8()     // 0x12
let word: UInt16 = try scanner.scanUInt16()   // 0x5634 (little-endian)
let dword: UInt32 = try scanner.scanUInt32()  // 0xDEBC9A78

// Scan arrays
scanner.position = 0
let bytes = try scanner.scanUInt8Array(count: 4)  // [0x12, 0x34, 0x56, 0x78]

String Scanning

let textData = "Hello\0World".data(using: .utf8)!
let scanner = DataScanner(data: textData)

// Scan null-terminated string
let greeting = try scanner.scanString(length: 6, nullTerminated: true)  // "Hello"

// Scan fixed-length string
let remaining = try scanner.scanString(length: 5)  // "World"

IFF File Format

let iffData = // ... your IFF file data
let scanner = IFFScanner(data: iffData)  // Defaults to big-endian

// Read chunk header
let chunkID = try scanner.scanChunkID()    // e.g., "FORM"
let chunkSize = try scanner.scanChunkSize() // Size in bytes

// Validate specific chunk
try scanner.scanChunkID("ILBM")  // Throws if not matched
try scanner.scanChunkSize(expectedSize)

Serialization with PackableType

import BinaryDataKit

// All numeric types conform to PackableType by default
let value: Int32 = 0x12345678
let bytes = value.pack()  // [0x78, 0x56, 0x34, 0x12] on little-endian
let restored = Int32.unpack(bytes)  // 0x12345678

// Custom types
struct Point: PackableType {
    let x: Float32
    let y: Float32
}

let point = Point(x: 1.0, y: 2.0)
let pointBytes = point.pack()
let restoredPoint = Point.unpack(pointBytes)

Direct Data Access

let data = Data([0x12, 0x34, 0x56, 0x78])

// Extract values directly
let value16: UInt16 = data.scanValue(start: 0)  // 0x3412
let value32: UInt32 = data.scanValue(start: 0)  // 0x78563412

// Extract arrays
let bytes: [UInt8] = data.scanValues(start: 0, count: 4)

API Reference

DataScanner

The main class for sequential binary data scanning:

Initialization

DataScanner(data: Data, endianness: .little/.big, startPosition: Int = 0)

Core Methods

  • scanValue<T>() throws -> T - Scan any type
  • scanValues<T>(_ count: Int) throws -> [T] - Scan array of values
  • scanData(length: Int) throws -> Data - Scan raw data
  • scanString(length: Int, encoding: .utf8, nullTerminated: Bool) throws -> String

Numeric Types

  • scanInt8/16/32/64() throws -> IntN
  • scanUInt8/16/32/64() throws -> UIntN
  • scanFloat16/32/64() throws -> FloatN
  • Array versions: scanInt8Array(count:), etc.

Endianness

  • All scanEndianedValue methods respect the scanner's endianness setting
  • Non-endianed methods read data as-is

IFFScanner

Extends DataScanner for IFF file format:

IFFScanner(data: Data, endianness: .big, startPosition: Int = 0)
scanChunkID() throws -> String
scanChunkSize() throws -> Int

PackableType Protocol

protocol PackableType {
    func pack() -> [UInt8]
    static func unpack(_ bytes: [UInt8]) -> Self
}

Built-in conformance for: Int, Int8-64, UInt, UInt8-64, Float, Double

Error Handling

BinaryDataKit defines specific error types:

DataScanner.Error.outOfRange                // Read beyond data bounds
DataScanner.Error.notValidString            // Invalid string encoding
DataScanner.Error.requiredValueDoesNotMatch // Value validation failed

Platform Support

  • iOS 14.0+
  • macOS 12.0+
  • tvOS 14.0+
  • visionOS 1.0+
  • Swift 5.10+

License

BinaryDataKit is available under the BSD Zero Clause License. See LICENSE for details.