Created
April 15, 2016 03:03
-
-
Save joshuadutton/294a2f1beefeaa5414ca87faec3ef56f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum NSDataError: ErrorType { | |
case OutOfBounds(data: NSData) | |
case InvalidASCIIString(data: NSData) | |
case ValueDoesNotMatchEnum(value: UInt8, type: Any) | |
} | |
// MARK: - NSData CollectionType conformance | |
extension NSData: CollectionType { | |
public subscript (position: Int) -> UInt8 { | |
return UnsafePointer<UInt8>(bytes)[position] | |
} | |
public subscript (bounds: Range<Int>) -> NSData { | |
return subdataWithRange(NSRange(bounds)) | |
} | |
public var startIndex: Int { return 0 } | |
public var endIndex: Int { return length } | |
} | |
extension NSData { | |
/// String formated with the hex value for each of the receiver's bytes separated by a colon (e.g. 01:23:45:67:89:ab) | |
var colonSeparatedRepresentation: String { | |
let byteStrings = self.map { return String(format: "%02x", $0) } | |
return byteStrings.joinWithSeparator(":") | |
} | |
/// Returns a data object containing the receiver’s bytes that start at location and end at location + length. | |
/// If location and length are out of bounds, it raises an `NSRangeException` | |
func subdataAtLocation(location: Int, length: Int) -> NSData { | |
return subdataWithRange(NSRange(location: location, length: length)) | |
} | |
/// Returns a data object containing the receiver’s bytes that start at location and end at location + length | |
/// Throws an error if the given location and length are out of bounds. | |
func readSubdataAtLocation(location: Int, length: Int) throws -> NSData { | |
if self.length < location + length { | |
throw NSDataError.OutOfBounds(data: self) | |
} | |
return subdataWithRange(NSRange(location: location, length: length)) | |
} | |
/// Returns a data object containing the receiver’s bytes that are in the range | |
/// Throws an error if the given range is out of bounds. | |
func readSubdataInRange(range: Range<Int>) throws -> NSData { | |
if range.endIndex > self.length { | |
throw NSDataError.OutOfBounds(data: self) | |
} | |
return self[range] | |
} | |
/// Returns an instance of T read from the reciever at the given location. | |
/// Example: if T == Int32, this will read 4 bytes starting at index and return an Int32 | |
/// Warning: the length of Int is hardware specific. | |
/// Throws an error if the location + the length of type T is out of bounds. | |
func readAtLocation<T>(location: Int) throws -> T { | |
return try readAtLocation(location, length: sizeof(T)) | |
} | |
func readAtLocation<T>(location: Int, length: Int) throws -> T { | |
let data = try readSubdataAtLocation(location, length: length) | |
return UnsafePointer<T>(data.bytes).memory | |
} | |
/// Returns an instance of T read from the reciever at the given location | |
/// when T is a RawRepresentable type and T.RawValue is a byte (UInt8). | |
/// Throws an error if the location is out of bounds. | |
func readAtLocation<T: RawRepresentable where T.RawValue == UInt8>(location: Int) throws -> T { | |
let rawValue = try readByteAtLocation(location) | |
guard let anEnum = T(rawValue: rawValue) else { | |
throw NSDataError.ValueDoesNotMatchEnum(value: rawValue, type: T.self) | |
} | |
return anEnum | |
} | |
/// Returns the byte at the given location. | |
/// Throws an error if the location is out of bounds. | |
func readByteAtLocation(location: Int) throws -> UInt8 { | |
return try readAtLocation(location) | |
} | |
/// Returns an ASCII formatted string that starts at location and ends at location + length | |
/// Throws an error if the given location and length are out of bounds. | |
func readASCIIStringAtLocation(location: Int, length: Int) throws -> String { | |
let stringData = try self.readSubdataAtLocation(0, length: length) | |
guard let string = String(data: stringData, encoding: NSASCIIStringEncoding) else { | |
throw NSDataError.InvalidASCIIString(data: self) | |
} | |
return string | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment