Skip to content

Commit

Permalink
Merge pull request #17 from orchetect/dev
Browse files Browse the repository at this point in the history
Added `DataReader`
  • Loading branch information
orchetect authored Nov 5, 2021
2 parents b2d03c6 + 25987cb commit c73f707
Show file tree
Hide file tree
Showing 2 changed files with 319 additions and 0 deletions.
91 changes: 91 additions & 0 deletions Sources/OTCore/Abstractions/DataReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//
// DataReader.swift
// OTCore • https://github.com/orchetect/OTCore
//

import Foundation

/// Utility to facilitate sequential reading of bytes.
public struct DataReader {

public let base: Data

public init(_ data: Data) {

base = data

}

/// Current byte index of read position
public internal(set) var readPosition = 0

/// Resets read position back to 0
public mutating func reset() {

readPosition = 0

}

/// Manually advance by n number of bytes from current read position
public mutating func advanceBy(_ count: Int) {

readPosition += count

}

/// Return the next _n_ number of bytes and increment the read position.
///
/// If `bytes` parameter is nil, the remainder of the data will be returned.
///
/// If fewer bytes remain than are requested, `nil` will be returned.
public mutating func read(bytes count: Int? = nil) -> Data? {

if count == 0 { return Data() }

if let count = count,
count < 0 { return nil }

let readPosStartIndex = base.startIndex.advanced(by: readPosition)

let count = count ?? (base.count - readPosition)

let endIndex = readPosStartIndex.advanced(by: count - 1)

guard base.indices.contains(readPosStartIndex),
base.indices.contains(endIndex) else { return nil }

let returnBytes = base[readPosStartIndex ... endIndex]

// advance read position
readPosition += count

return returnBytes

}

/// Read n number of bytes from current read position, without advancing read position.
/// If `bytes count` passed is nil, the remainder of the data will be returned.
/// If fewer bytes remain than are requested, `nil` will be returned.
public func nonAdvancingRead(bytes count: Int? = nil) -> Data? {

if count == 0 { return Data() }

if let count = count,
count < 0 { return nil }

let readPosStartIndex = base.startIndex.advanced(by: readPosition)

let count = count ?? (base.count - readPosition)

let endIndex = readPosStartIndex.advanced(by: count - 1)

guard base.indices.contains(readPosStartIndex),
base.indices.contains(endIndex) else { return nil }

let returnBytes = base[readPosStartIndex ... endIndex]

return returnBytes

}

}
228 changes: 228 additions & 0 deletions Tests/OTCoreTests/Abstractions/DataReader Tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
//
// DataReader Tests.swift
// OTCore • https://github.com/orchetect/OTCore
//

#if !os(watchOS)

import XCTest
@testable import OTCore

class Abstractions_DataReader_Tests: XCTestCase {

// MARK: - Data storage starting with index 0

func testRead() {

let data = Data([0x01, 0x02, 0x03, 0x04])

// .read - byte by byte
do {
var dr = DataReader(data)

XCTAssertEqual(dr.readPosition, 0)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x02]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x03]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x04]))
XCTAssertEqual(dr.read(bytes: 1), nil)
}

// .read - nil read - return all remaining bytes
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(), data)
XCTAssertEqual(dr.read(bytes: 1), nil)
}

// .read - zero count read - return empty data, not nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 0), Data())
}

// .read - read overflow - return nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 5), nil)
}

}

func testNonAdvancingRead() {

let data = Data([0x01, 0x02, 0x03, 0x04])

// .nonAdvancingRead - nil read - return all remaining bytes
do {
var dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(), data)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

// .nonAdvancingRead - read byte counts
do {
let dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.nonAdvancingRead(bytes: 2), Data([0x01, 0x02]))
}

// .nonAdvancingRead - read overflow - return nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(bytes: 5), nil)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

}

func testAdvanceBy() {

let data = Data([0x01, 0x02, 0x03, 0x04])

// advanceBy
do {
var dr = DataReader(data)

dr.advanceBy(1)
XCTAssertEqual(dr.read(bytes: 1), Data([0x02]))
}

}

func tesReset() {

let data = Data([0x01, 0x02, 0x03, 0x04])

// reset
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.read(bytes: 2), Data([0x02, 0x03]))
dr.reset()
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

}

// MARK: - Data storage starting with index >0

func testRead_DataIndicesOffset() {

let rawData = Data([0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04])
let data = rawData[3 ... 6]

// .read - byte by byte
do {
var dr = DataReader(data)

XCTAssertEqual(dr.readPosition, 0)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x02]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x03]))
XCTAssertEqual(dr.read(bytes: 1), Data([0x04]))
XCTAssertEqual(dr.read(bytes: 1), nil)
}

// .read - nil read - return all remaining bytes
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(), data)
XCTAssertEqual(dr.read(bytes: 1), nil)
}

// .read - zero count read - return empty data, not nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 0), Data())
}

// .read - read overflow - return nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 5), nil)
}

}

func testNonAdvancingRead_DataIndicesOffset() {

let rawData = Data([0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04])
let data = rawData[3 ... 6]

// .nonAdvancingRead - nil read - return all remaining bytes
do {
var dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(), data)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

// .nonAdvancingRead - read byte counts
do {
let dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.nonAdvancingRead(bytes: 2), Data([0x01, 0x02]))
}

// .nonAdvancingRead - read overflow - return nil
do {
var dr = DataReader(data)

XCTAssertEqual(dr.nonAdvancingRead(bytes: 5), nil)
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

}

func testAdvanceBy_DataIndicesOffset() {

let rawData = Data([0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04])
let data = rawData[3 ... 6]

// advanceBy
do {
var dr = DataReader(data)

dr.advanceBy(1)
XCTAssertEqual(dr.read(bytes: 1), Data([0x02]))
}

}

func tesReset_DataIndicesOffset() {

let rawData = Data([0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04])
let data = rawData[3 ... 6]


// reset
do {
var dr = DataReader(data)

XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
XCTAssertEqual(dr.read(bytes: 2), Data([0x02, 0x03]))
dr.reset()
XCTAssertEqual(dr.read(bytes: 1), Data([0x01]))
}

}

}

#endif

0 comments on commit c73f707

Please sign in to comment.