├── .gitignore ├── LICENSE ├── Package.swift ├── Sources └── SwiftNpy │ ├── DataType.swift │ ├── Npy.swift │ ├── NpyHeader.swift │ ├── NpyLoader.swift │ ├── NpySaver.swift │ ├── Npz.swift │ ├── NpzLoader.swift │ └── NpzSaver.swift ├── Tests ├── LinuxMain.swift └── SwiftNpyTests │ ├── NpyLoaderTests.swift │ ├── NpySaverTests.swift │ ├── NpzLoaderTests.swift │ └── NpzSaverTests.swift └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | .swiftpm 6 | Package.resolved 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Qoncept, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SwiftNpy", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "SwiftNpy", 12 | targets: ["SwiftNpy"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | .package(url: "https://github.com/SwiftZip/SwiftZip.git", from: "0.0.5"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 22 | .target( 23 | name: "SwiftNpy", 24 | dependencies: ["SwiftZip"]), 25 | .testTarget( 26 | name: "SwiftNpyTests", 27 | dependencies: ["SwiftNpy"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/DataType.swift: -------------------------------------------------------------------------------- 1 | 2 | public enum DataType: String { 3 | case bool = "b1" 4 | 5 | case uint8 = "u1" 6 | case uint16 = "u2" 7 | case uint32 = "u4" 8 | case uint64 = "u8" 9 | 10 | case int8 = "i1" 11 | case int16 = "i2" 12 | case int32 = "i4" 13 | case int64 = "i8" 14 | 15 | case float32 = "f4" 16 | case float64 = "f8" 17 | 18 | static var all: [DataType] { 19 | return [.bool, 20 | .uint8, .uint16, .uint32, .uint64, 21 | .int8, .int16, .int32, .int64, 22 | .float32, .float64] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/Npy.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Npy { 4 | 5 | let header: NpyHeader 6 | let elementsData: Data 7 | 8 | public var shape: [Int] { 9 | return header.shape 10 | } 11 | 12 | var elementsCount: Int { 13 | return shape.reduce(1, *) 14 | } 15 | 16 | public var dataType: DataType { 17 | return header.dataType 18 | } 19 | 20 | public var endian: Endian { 21 | return header.endian 22 | } 23 | 24 | public var isFortranOrder: Bool { 25 | return header.isFortranOrder 26 | } 27 | 28 | init(header: NpyHeader, elementsData: Data) { 29 | self.elementsData = elementsData 30 | self.header = header 31 | } 32 | } 33 | 34 | extension Npy { 35 | public init(shape: [Int], elements: [Bool], isFortranOrder: Bool) { 36 | precondition(shape.reduce(1, *) == elements.count) 37 | let header = NpyHeader(shape: shape, dataType: .bool, endian: .na, isFortranOrder: isFortranOrder) 38 | let data = Data(elements.map { $0 ? 0x01 : 0x00 }) 39 | self.init(header: header, elementsData: data) 40 | } 41 | 42 | public init(shape: [Int], elements: [UInt8], isFortranOrder: Bool) { 43 | precondition(shape.reduce(1, *) == elements.count) 44 | let header = NpyHeader(shape: shape, dataType: .uint8, endian: .na, isFortranOrder: isFortranOrder) 45 | let data = Data(elements) 46 | self.init(header: header, elementsData: data) 47 | } 48 | 49 | public init(shape: [Int], elements: [UInt16], endian: Endian, isFortranOrder: Bool) { 50 | precondition(shape.reduce(1, *) == elements.count) 51 | let header = NpyHeader(shape: shape, dataType: .uint16, endian: endian, isFortranOrder: isFortranOrder) 52 | let data = toData(elements: elements, endian: header.endian) 53 | self.init(header: header, elementsData: data) 54 | 55 | } 56 | 57 | public init(shape: [Int], elements: [UInt32], endian: Endian, isFortranOrder: Bool) { 58 | precondition(shape.reduce(1, *) == elements.count) 59 | let header = NpyHeader(shape: shape, dataType: .uint32, endian: endian, isFortranOrder: isFortranOrder) 60 | let data = toData(elements: elements, endian: header.endian) 61 | self.init(header: header, elementsData: data) 62 | } 63 | 64 | public init(shape: [Int], elements: [UInt64], endian: Endian, isFortranOrder: Bool) { 65 | precondition(shape.reduce(1, *) == elements.count) 66 | let header = NpyHeader(shape: shape, dataType: .uint64, endian: endian, isFortranOrder: isFortranOrder) 67 | let data = toData(elements: elements, endian: header.endian) 68 | self.init(header: header, elementsData: data) 69 | } 70 | 71 | public init(shape: [Int], elements: [Int8], isFortranOrder: Bool) { 72 | precondition(shape.reduce(1, *) == elements.count) 73 | let header = NpyHeader(shape: shape, dataType: .int8, endian: .na, isFortranOrder: isFortranOrder) 74 | let uints = elements.map { UInt8(bitPattern: $0) } 75 | let data = Data(uints) 76 | self.init(header: header, elementsData: data) 77 | } 78 | 79 | public init(shape: [Int], elements: [Int16], endian: Endian, isFortranOrder: Bool) { 80 | precondition(shape.reduce(1, *) == elements.count) 81 | let header = NpyHeader(shape: shape, dataType: .int16, endian: endian, isFortranOrder: isFortranOrder) 82 | let uints = elements.map { UInt16(bitPattern: $0) } 83 | let data = toData(elements: uints, 84 | endian: header.endian) 85 | self.init(header: header, elementsData: data) 86 | } 87 | 88 | public init(shape: [Int], elements: [Int32], endian: Endian, isFortranOrder: Bool) { 89 | precondition(shape.reduce(1, *) == elements.count) 90 | let header = NpyHeader(shape: shape, dataType: .int32, endian: endian, isFortranOrder: isFortranOrder) 91 | let uints = elements.map { UInt32(bitPattern: $0) } 92 | let data = toData(elements: uints, 93 | endian: header.endian) 94 | self.init(header: header, elementsData: data) 95 | } 96 | 97 | public init(shape: [Int], elements: [Int64], endian: Endian, isFortranOrder: Bool) { 98 | precondition(shape.reduce(1, *) == elements.count) 99 | let header = NpyHeader(shape: shape, dataType: .int64, endian: endian, isFortranOrder: isFortranOrder) 100 | let uints = elements.map { UInt64(bitPattern: $0) } 101 | let data = toData(elements: uints, 102 | endian: header.endian) 103 | self.init(header: header, elementsData: data) 104 | } 105 | 106 | public init(shape: [Int], elements: [Float], endian: Endian, isFortranOrder: Bool) { 107 | precondition(shape.reduce(1, *) == elements.count) 108 | let header = NpyHeader(shape: shape, dataType: .float32, endian: endian, isFortranOrder: isFortranOrder) 109 | let uints = elements.map { $0.bitPattern } 110 | let data = toData(elements: uints, 111 | endian: header.endian) 112 | self.init(header: header, elementsData: data) 113 | } 114 | 115 | public init(shape: [Int], elements: [Double], endian: Endian, isFortranOrder: Bool) { 116 | precondition(shape.reduce(1, *) == elements.count) 117 | let header = NpyHeader(shape: shape, dataType: .float64, endian: endian, isFortranOrder: isFortranOrder) 118 | let uints = elements.map { $0.bitPattern } 119 | let data = toData(elements: uints, 120 | endian: header.endian) 121 | self.init(header: header, elementsData: data) 122 | } 123 | } 124 | 125 | extension Npy { 126 | public func elements(_ type: Bool.Type = Bool.self) -> [Bool] { 127 | precondition(dataType == .bool) 128 | let uints = loadUInt8s(data: elementsData, count: elementsCount) 129 | return uints.map { $0 != 0 } 130 | } 131 | 132 | public func elements(_ type: UInt.Type = UInt.self) -> [UInt] { 133 | switch dataType { 134 | case .uint8: 135 | let uints = loadUInt8s(data: elementsData, count: elementsCount) 136 | return uints.map { UInt($0) } 137 | case .uint16: 138 | let uints: [UInt16] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 139 | return uints.map { UInt($0) } 140 | case .uint32: 141 | let uints: [UInt32] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 142 | return uints.map { UInt($0) } 143 | case .uint64: 144 | let uints: [UInt64] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 145 | return uints.map { UInt($0) } 146 | default: 147 | preconditionFailure() 148 | } 149 | } 150 | 151 | public func elements(_ type: UInt8.Type = UInt8.self) -> [UInt8] { 152 | precondition(dataType == .uint8) 153 | let uints = loadUInt8s(data: elementsData, count: elementsCount) 154 | return uints 155 | } 156 | 157 | public func elements(_ type: UInt16.Type = UInt16.self) -> [UInt16] { 158 | precondition(dataType == .uint16) 159 | let uints: [UInt16] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 160 | return uints 161 | } 162 | 163 | public func elements(_ type: UInt32.Type = UInt32.self) -> [UInt32] { 164 | precondition(dataType == .uint32) 165 | let uints: [UInt32] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 166 | return uints 167 | } 168 | 169 | public func elements(_ type: UInt64.Type = UInt64.self) -> [UInt64] { 170 | precondition(dataType == .uint64) 171 | let uints: [UInt64] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 172 | return uints 173 | } 174 | 175 | public func elements(_ type: Int.Type = Int.self) -> [Int] { 176 | switch dataType { 177 | case .int8: 178 | let uints = loadUInt8s(data: elementsData, count: elementsCount) 179 | return uints.map { Int(Int8(bitPattern: $0)) } 180 | case .int16: 181 | let uints: [UInt16] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 182 | return uints.map { Int(Int16(bitPattern: $0)) } 183 | case .int32: 184 | let uints: [UInt32] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 185 | return uints.map { Int(Int32(bitPattern: $0)) } 186 | case .int64: 187 | let uints: [UInt64] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 188 | return uints.map { Int(Int64(bitPattern: $0)) } 189 | default: 190 | preconditionFailure() 191 | } 192 | } 193 | 194 | public func elements(_ type: Int8.Type = Int8.self) -> [Int8] { 195 | precondition(dataType == .int8) 196 | let uints = loadUInt8s(data: elementsData, count: elementsCount) 197 | return uints.map { Int8(bitPattern: $0) } 198 | } 199 | 200 | public func elements(_ type: Int16.Type = Int16.self) -> [Int16] { 201 | precondition(dataType == .int16) 202 | let uints: [UInt16] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 203 | return uints.map { Int16(bitPattern: $0) } 204 | } 205 | 206 | public func elements(_ type: Int32.Type = Int32.self) -> [Int32] { 207 | precondition(dataType == .int32) 208 | let uints: [UInt32] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 209 | return uints.map { Int32(bitPattern: $0) } 210 | } 211 | 212 | public func elements(_ type: Int64.Type = Int64.self) -> [Int64] { 213 | precondition(dataType == .int64) 214 | let uints: [UInt64] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 215 | return uints.map { Int64(bitPattern: $0) } 216 | } 217 | 218 | public func elements(_ type: Float.Type = Float.self) -> [Float] { 219 | precondition(dataType == .float32) 220 | let uints: [UInt32] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 221 | return uints.map { Float(bitPattern: $0) } 222 | } 223 | 224 | public func elements(_ type: Double.Type = Double.self) -> [Double] { 225 | precondition(dataType == .float64) 226 | let uints: [UInt64] = loadUInts(data: elementsData, count: elementsCount, endian: endian) 227 | return uints.map { Double(bitPattern: $0) } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/NpyHeader.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | 4 | let MAGIC_PREFIX = Data([0x93]) + "NUMPY".data(using: .ascii)! 5 | 6 | struct NpyHeader { 7 | let shape: [Int] 8 | let dataType: DataType 9 | let endian: Endian 10 | let isFortranOrder: Bool 11 | let descr: String 12 | 13 | init(shape: [Int], dataType: DataType, endian: Endian, isFortranOrder: Bool, descr: String) { 14 | self.shape = shape 15 | self.dataType = dataType 16 | self.endian = endian 17 | self.isFortranOrder = isFortranOrder 18 | self.descr = descr 19 | } 20 | 21 | init(shape: [Int], dataType: DataType, endian: Endian, isFortranOrder: Bool) { 22 | let descr = "'" + endian.rawValue + dataType.rawValue + "'" 23 | self.init(shape: shape, 24 | dataType: dataType, 25 | endian: endian, 26 | isFortranOrder: isFortranOrder, 27 | descr: descr) 28 | } 29 | } 30 | 31 | // https://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.byteorder.html 32 | public enum Endian: String { 33 | case host = "=" 34 | case big = ">" 35 | case little = "<" 36 | case na = "|" 37 | 38 | static var all: [Endian] { 39 | return [.host, .big, .little, .na] 40 | } 41 | } 42 | 43 | func parseHeader(_ data: Data) throws -> NpyHeader { 44 | 45 | guard let str = String(data: data, encoding: .ascii) else { 46 | throw NpyLoaderError.ParseFailed(message: "Failed to load header") 47 | } 48 | 49 | let descr: String 50 | let endian: Endian 51 | let dataType: DataType 52 | let isFortranOrder: Bool 53 | do { 54 | let separate = str.components(separatedBy: CharacterSet(charactersIn: ", ")).filter { !$0.isEmpty } 55 | 56 | guard let descrIndex = separate.index(where: { $0.contains("descr") }) else { 57 | throw NpyLoaderError.ParseFailed(message: "Header does not contain the key 'descr'") 58 | } 59 | descr = separate[descrIndex + 1] 60 | 61 | guard let e = Endian.all.filter({ descr.contains($0.rawValue) }).first else { 62 | throw NpyLoaderError.ParseFailed(message: "Unknown endian") 63 | } 64 | endian = e 65 | 66 | guard let dt = DataType.all.filter({ descr.contains($0.rawValue) }).first else { 67 | fatalError("Unsupported dtype: \(descr)") 68 | } 69 | dataType = dt 70 | 71 | guard let fortranIndex = separate.index(where: { $0.contains("fortran_order") }) else { 72 | throw NpyLoaderError.ParseFailed(message: "Header does not contain the key 'fortran_order'") 73 | } 74 | 75 | isFortranOrder = separate[fortranIndex+1].contains("True") 76 | } 77 | 78 | var shape: [Int] = [] 79 | do { 80 | guard let left = str.range(of: "("), 81 | let right = str.range(of: ")") else { 82 | throw NpyLoaderError.ParseFailed(message: "Shape not found in header.") 83 | } 84 | 85 | let substr = str[left.upperBound.. Data { 106 | let fortran_order = header.isFortranOrder ? "True" : "False" 107 | let shape: String 108 | switch header.shape.count { 109 | case 0: 110 | shape = "()" 111 | case 1: 112 | shape = "(\(header.shape[0]),)" 113 | default: 114 | shape = "(" + header.shape.map(String.init).joined(separator: ", ") + ")" 115 | } 116 | 117 | let str = "{ 'descr': \(header.descr), 'fortran_order': \(fortran_order), 'shape': \(shape), }" 118 | return str.data(using: .ascii)! 119 | } 120 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/NpyLoader.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | 4 | // https://docs.scipy.org/doc/numpy-dev/neps/npy-format.html 5 | 6 | extension Npy { 7 | public init(contentsOf url: URL) throws { 8 | let data = try Data(contentsOf: url) 9 | try self.init(data: data) 10 | } 11 | 12 | public init(data: Data) throws { 13 | let magic = data.subdata(in: 0..<6) 14 | guard magic == MAGIC_PREFIX else { 15 | throw NpyLoaderError.ParseFailed(message: "Invalid prefix: \(magic)") 16 | } 17 | 18 | let major = data[6] 19 | guard major == 1 || major == 2 else { 20 | throw NpyLoaderError.ParseFailed(message: "Invalid major version: \(major)") 21 | } 22 | 23 | let minor = data[7] 24 | guard minor == 0 else { 25 | throw NpyLoaderError.ParseFailed(message: "Invalid minor version: \(minor)") 26 | } 27 | 28 | let headerLen: Int 29 | let rest: Data 30 | switch major { 31 | case 1: 32 | let tmp = Data(data[8...9]).withUnsafeBytes { (ptr: UnsafePointer) in 33 | ptr.withMemoryRebound(to: UInt16.self, capacity: 1) { 34 | UInt16(littleEndian: $0.pointee) 35 | } 36 | } 37 | headerLen = Int(tmp) 38 | rest = data.subdata(in: 10..) in 41 | ptr.withMemoryRebound(to: UInt32.self, capacity: 1) { 42 | UInt32(littleEndian: $0.pointee) 43 | } 44 | } 45 | headerLen = Int(tmp) 46 | rest = data.subdata(in: 12..(data: Data, count: Int, endian: Endian) -> [T] { 74 | 75 | switch endian { 76 | case .host: 77 | let uints = data.withUnsafeBytes { (ptr: UnsafePointer) in 78 | ptr.withMemoryRebound(to: T.self, capacity: count) { ptr2 in 79 | [T](UnsafeBufferPointer(start: ptr2, count: count)) 80 | } 81 | } 82 | return uints 83 | case .big: 84 | return data.withUnsafeBytes { (ptr: UnsafePointer) in 85 | ptr.withMemoryRebound(to: T.self, capacity: count) { ptr2 in 86 | (0..) in 91 | ptr.withMemoryRebound(to: T.self, capacity: count) { ptr2 in 92 | (0.. [UInt8] { 101 | let uints = data.withUnsafeBytes { (ptr: UnsafePointer) in 102 | [UInt8](UnsafeBufferPointer(start: ptr, count: count)) 103 | } 104 | return uints 105 | } 106 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/NpySaver.swift: -------------------------------------------------------------------------------- 1 | import CoreFoundation 2 | import Foundation 3 | 4 | extension Npy { 5 | public func save(to url: URL) throws { 6 | let data = self.format() 7 | try data.write(to: url) 8 | } 9 | 10 | public func format() -> Data { 11 | var data = Data() 12 | 13 | data.append(contentsOf: MAGIC_PREFIX) 14 | 15 | let header = encodeHeader(self.header) 16 | 17 | if header.count > 65535 { 18 | // v2 19 | data.append(0x02) 20 | data.append(0x00) 21 | var headerLen = UInt32(header.count).littleEndian 22 | withUnsafePointer(to: &headerLen) { p in 23 | p.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size) { 24 | data.append($0, count: MemoryLayout.size) 25 | } 26 | } 27 | } else { 28 | // v1 29 | data.append(0x01) 30 | data.append(0x00) 31 | var headerLen = UInt16(header.count).littleEndian 32 | withUnsafePointer(to: &headerLen) { p in 33 | p.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size) { 34 | data.append($0, count: MemoryLayout.size) 35 | } 36 | } 37 | } 38 | 39 | 40 | data.append(header) 41 | data.append(self.elementsData) 42 | 43 | return data 44 | } 45 | } 46 | 47 | func toData(elements: [UInt16], endian: Endian) -> Data { 48 | let uints: [UInt16] 49 | switch endian { 50 | case .host: 51 | uints = elements 52 | case .big: 53 | uints = elements.map(CFSwapInt16HostToBig) 54 | case .little: 55 | uints = elements.map(CFSwapInt16HostToLittle) 56 | case .na: 57 | fatalError("Invalid byteorder.") 58 | } 59 | let count = MemoryLayout.size * elements.count 60 | return Data(bytes: uints, count: count) 61 | } 62 | 63 | func toData(elements: [UInt32], endian: Endian) -> Data { 64 | let uints: [UInt32] 65 | switch endian { 66 | case .host: 67 | uints = elements 68 | case .big: 69 | uints = elements.map(CFSwapInt32HostToBig) 70 | case .little: 71 | uints = elements.map(CFSwapInt32HostToLittle) 72 | case .na: 73 | fatalError("Invalid byteorder.") 74 | } 75 | let count = MemoryLayout.size * elements.count 76 | return Data(bytes: uints, count: count) 77 | } 78 | 79 | func toData(elements: [UInt64], endian: Endian) -> Data { 80 | let uints: [UInt64] 81 | switch endian { 82 | case .host: 83 | uints = elements 84 | case .big: 85 | uints = elements.map(CFSwapInt64HostToBig) 86 | case .little: 87 | uints = elements.map(CFSwapInt64HostToLittle) 88 | case .na: 89 | fatalError("Invalid byteorder.") 90 | } 91 | let count = MemoryLayout.size * elements.count 92 | return Data(bytes: uints, count: count) 93 | } 94 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/Npz.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | 4 | public struct Npz { 5 | let dict: [String: Npy] 6 | 7 | public init(dict: [String: Npy]) { 8 | var npyDict = [String:Npy]() 9 | for (k, v) in dict { 10 | if k.hasSuffix(".npy") { 11 | npyDict[k] = v 12 | } else { 13 | npyDict[k+".npy"] = v 14 | } 15 | } 16 | 17 | self.dict = npyDict 18 | } 19 | 20 | public var keys: [String] { 21 | return dict.keys.map { 22 | precondition($0.hasSuffix(".npy")) 23 | return NSString(string: $0).deletingPathExtension 24 | } 25 | } 26 | 27 | public subscript(key: String) -> Npy? { 28 | let k: String 29 | if key.hasSuffix(".npy") { 30 | k = key 31 | } else { 32 | k = key + ".npy" 33 | } 34 | 35 | return dict[k] 36 | } 37 | } 38 | 39 | public enum NpzError: Error { 40 | case noSuchEntry 41 | } 42 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/NpzLoader.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import SwiftZip 4 | 5 | extension Npz { 6 | public init(contentsOf url: URL) throws { 7 | let data = try Data(contentsOf: url) 8 | try self.init(data: data) 9 | } 10 | 11 | public init(data: Data) throws { 12 | let source = try ZipSource(data: data) 13 | let archive = try ZipArchive(source: source) 14 | 15 | var dict = [String: Npy]() 16 | for entry in archive.entries() { 17 | let entryName = try entry.getName() 18 | let entryData = try entry.data() 19 | dict[entryName] = try Npy(data: entryData) 20 | } 21 | 22 | self.init(dict: dict) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/SwiftNpy/NpzSaver.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import SwiftZip 4 | 5 | extension Npz { 6 | public func save(to url: URL) throws { 7 | let archive = try ZipMutableArchive(url: url, flags: [.create, .truncate]) 8 | 9 | for (name, npy) in dict { 10 | let source = try ZipSource(data: npy.format()) 11 | try archive.addFile(name: name, source: source) 12 | } 13 | 14 | try archive.close() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftNpyTests 3 | 4 | XCTMain([ 5 | testCase(NpyLoaderTests.allTests), 6 | testCase(NpySaverTests.allTests), 7 | testCase(NpzLoaderTests.allTests), 8 | testCase(NpzSaverTests.allTests), 9 | ]) 10 | -------------------------------------------------------------------------------- /Tests/SwiftNpyTests/NpyLoaderTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftNpy 3 | 4 | class NpyLoaderTests: XCTestCase { 5 | 6 | func testLoadBool() { 7 | do { 8 | let npy: Npy = try! Npy(data: b1) 9 | XCTAssertEqual(npy.shape, [2]) 10 | let elements: [Bool] = npy.elements() 11 | XCTAssertEqual(elements, [true, false]) 12 | } 13 | } 14 | 15 | func testLoadUInt() { 16 | do { 17 | let npy: Npy = try! Npy(data: u1) 18 | XCTAssertEqual(npy.shape, [2, 3]) 19 | let elements: [UInt8] = npy.elements() 20 | XCTAssertEqual(elements, [253, 254, 255, 0, 1, 2]) 21 | } 22 | do { 23 | let npy: Npy = try! Npy(data: u1) 24 | XCTAssertEqual(npy.shape, [2, 3]) 25 | let elements: [UInt] = npy.elements() 26 | XCTAssertEqual(elements, [253, 254, 255, 0, 1, 2]) 27 | } 28 | do { 29 | let npy: Npy = try! Npy(data: u2) 30 | XCTAssertEqual(npy.shape, [3, 2]) 31 | let elements = npy.elements(UInt16.self) 32 | XCTAssertEqual(elements, [65533, 65534,65535, 0, 1, 2]) 33 | } 34 | do { 35 | let npy: Npy = try! Npy(data: u2) 36 | XCTAssertEqual(npy.shape, [3, 2]) 37 | let elements: [UInt] = npy.elements() 38 | XCTAssertEqual(elements, [65533, 65534,65535, 0, 1, 2]) 39 | } 40 | do { 41 | let npy: Npy = try! Npy(data: u4) 42 | XCTAssertEqual(npy.shape, [3, 2]) 43 | let elements: [UInt32] = npy.elements() 44 | XCTAssertEqual(elements, [4294967293, 4294967294,4294967295, 0, 1, 2]) 45 | } 46 | do { 47 | let npy: Npy = try! Npy(data: u4) 48 | XCTAssertEqual(npy.shape, [3, 2]) 49 | let elements: [UInt] = npy.elements() 50 | XCTAssertEqual(elements, [4294967293, 4294967294,4294967295, 0, 1, 2]) 51 | } 52 | do { 53 | let npy: Npy = try! Npy(data: u8) 54 | XCTAssertEqual(npy.shape, [2, 3]) 55 | let elements: [UInt64] = npy.elements() 56 | XCTAssertEqual(elements, [18446744073709551613, 18446744073709551614, 18446744073709551615, 0, 1, 2]) 57 | } 58 | do { 59 | let npy: Npy = try! Npy(data: u8) 60 | XCTAssertEqual(npy.shape, [2, 3]) 61 | let elements: [UInt] = npy.elements() 62 | XCTAssertEqual(elements, [18446744073709551613, 18446744073709551614, 18446744073709551615, 0, 1, 2]) 63 | } 64 | } 65 | 66 | func testLoadInt() { 67 | do { 68 | let npy: Npy = try! Npy(data: i1) 69 | XCTAssertEqual(npy.shape, [2, 3]) 70 | let elements: [Int8] = npy.elements() 71 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 72 | } 73 | do { 74 | let npy: Npy = try! Npy(data: i1) 75 | XCTAssertEqual(npy.shape, [2, 3]) 76 | let elements: [Int] = npy.elements() 77 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 78 | } 79 | do { 80 | let npy: Npy = try! Npy(data: i2) 81 | XCTAssertEqual(npy.shape, [2, 3]) 82 | let elements: [Int16] = npy.elements() 83 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 84 | } 85 | do { 86 | let npy: Npy = try! Npy(data: i2) 87 | XCTAssertEqual(npy.shape, [2, 3]) 88 | let elements: [Int] = npy.elements() 89 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 90 | } 91 | do { 92 | let npy: Npy = try! Npy(data: i4) 93 | XCTAssertEqual(npy.shape, [3, 2]) 94 | let elements: [Int32] = npy.elements() 95 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 96 | } 97 | do { 98 | let npy: Npy = try! Npy(data: i4) 99 | XCTAssertEqual(npy.shape, [3, 2]) 100 | let elements: [Int] = npy.elements() 101 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 102 | } 103 | do { 104 | let npy: Npy = try! Npy(data: i8) 105 | XCTAssertEqual(npy.shape, [3, 2, 1]) 106 | let elements: [Int64] = npy.elements() 107 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 108 | } 109 | do { 110 | let npy: Npy = try! Npy(data: i8) 111 | XCTAssertEqual(npy.shape, [3, 2, 1]) 112 | let elements: [Int] = npy.elements() 113 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 114 | } 115 | } 116 | 117 | func testLoadFloatDouble() { 118 | do { 119 | let npy: Npy = try! Npy(data: f4) 120 | XCTAssertEqual(npy.shape, [1, 1, 6]) 121 | let elements: [Float] = npy.elements() 122 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 123 | } 124 | do { 125 | let npy: Npy = try! Npy(data: f8) 126 | XCTAssertEqual(npy.shape, [1, 6, 1]) 127 | let elements: [Double] = npy.elements() 128 | XCTAssertEqual(elements, [-3, -2, -1, 0, 1, 2]) 129 | } 130 | 131 | } 132 | 133 | func testLoadSpecialCase() { 134 | do { 135 | let npy: Npy = try! Npy(data: f8_empty) 136 | XCTAssertEqual(npy.shape, [0]) 137 | let elements: [Double] = npy.elements() 138 | XCTAssertEqual(elements, []) 139 | } 140 | do { 141 | let npy: Npy = try! Npy(data: i8_scalar) 142 | XCTAssertEqual(npy.shape, []) 143 | let elements: [Int] = npy.elements() 144 | XCTAssertEqual(elements, [3]) 145 | } 146 | } 147 | 148 | static var allTests = [ 149 | ("testLoadBool", testLoadBool), 150 | ("testLoadUInt", testLoadUInt), 151 | ("testLoadInt", testLoadInt), 152 | ("testLoadFloatDouble", testLoadFloatDouble), 153 | ("testLoadSpecialCase", testLoadSpecialCase), 154 | ] 155 | } 156 | 157 | /// [true, false] 158 | let b1 = Data(bytes: [ 159 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 160 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x7c, 0x62, 0x31, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 161 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 162 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x32, 0x2c, 0x29, 163 | 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 164 | 0x01, 0x00 165 | ]) 166 | 167 | /// [[253, 254, 255], [ 0, 1, 2]] 168 | let u1 = Data(bytes: [ 169 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 170 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x7c, 0x75, 0x31, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 171 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 172 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x32, 0x2c, 0x20, 173 | 0x33, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 174 | 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02 175 | ]) 176 | 177 | /// [[65533, 65534],[65535, 0],[ 1, 2]] big endian 178 | let u2 = Data(bytes: [ 179 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 180 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3e, 0x75, 0x32, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 181 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 182 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x33, 0x2c, 0x20, 183 | 0x32, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 184 | 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 185 | ]) 186 | 187 | /// [[4294967293, 4294967294],[4294967295, 0],[ 1, 2]] 188 | let u4 = Data(bytes: [ 189 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 190 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x75, 0x34, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 191 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 192 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x33, 0x2c, 0x20, 193 | 0x32, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 194 | 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 195 | 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 196 | ]) 197 | 198 | /// [[18446744073709551613, 18446744073709551614, 18446744073709551615],[ 0, 1, 2]] big endian 199 | let u8 = Data(bytes: [ 200 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 201 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3e, 0x75, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 202 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 203 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x32, 0x2c, 0x20, 204 | 0x33, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 205 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 206 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 208 | ]) 209 | 210 | /// [[-3, -2, -1],[ 0, 1, 2]] 211 | let i1 = Data(bytes: [ 212 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 213 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x7c, 0x69, 0x31, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 214 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 215 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x32, 0x2c, 0x20, 216 | 0x33, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 217 | 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02 218 | ]) 219 | 220 | /// [[-3, -2],[-1, 0],[ 1, 2]] 221 | let i2 = Data(bytes: [ 222 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 223 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x69, 0x32, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 224 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 225 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x32, 0x2c, 0x20, 226 | 0x33, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 227 | 0xfd, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00 228 | ]) 229 | 230 | /// [[-3, -2],[-1, 0],[ 1, 2]] big endian 231 | let i4 = Data(bytes: [ 232 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 233 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3e, 0x69, 0x34, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 234 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 235 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x33, 0x2c, 0x20, 236 | 0x32, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 237 | 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 238 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02 239 | ]) 240 | 241 | /// [[[-3], [-2]],[[-1], [0]],[ [1], [2]]] big endian 242 | let i8 = Data(bytes: [ 243 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 244 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3e, 0x69, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 245 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 246 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x33, 0x2c, 0x20, 247 | 0x32, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 248 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 249 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 250 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 251 | ]) 252 | 253 | /// [[[-3, -2, -1, 0., 1, 2]]] 254 | let f4 = Data(bytes: [ 255 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 256 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x66, 0x34, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 257 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 258 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x31, 0x2c, 0x20, 259 | 0x31, 0x2c, 0x20, 0x36, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 260 | 0x00, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0x00, 261 | 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40 262 | ]) 263 | 264 | /// [[[-3], [-2], [-1], [0], [1], [2]]] big endian 265 | let f8 = Data(bytes: [ 266 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 267 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3e, 0x66, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 268 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 269 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x31, 0x2c, 0x20, 270 | 0x36, 0x2c, 0x20, 0x31, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 271 | 0xc0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 272 | 0xbf, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 273 | 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 274 | ]) 275 | 276 | let f8_empty = Data(bytes: [ 277 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 278 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x66, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 279 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 280 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x30, 0x2c, 0x29, 281 | 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a 282 | ]) 283 | 284 | let i8_scalar = Data(bytes: [ 285 | 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 286 | 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x69, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 287 | 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 288 | 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x29, 0x2c, 0x20, 289 | 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 290 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 291 | ]) 292 | -------------------------------------------------------------------------------- /Tests/SwiftNpyTests/NpySaverTests.swift: -------------------------------------------------------------------------------- 1 | 2 | import XCTest 3 | import SwiftNpy 4 | 5 | class NpySaverTests: XCTestCase { 6 | 7 | func testFormatB1() { 8 | let elements = [true, false] 9 | let npy = Npy(shape: [2], elements: elements, isFortranOrder: false) 10 | let data = npy.format() 11 | let npy2: Npy = try! Npy(data: data) 12 | 13 | XCTAssertEqual(npy.shape, npy2.shape) 14 | 15 | let e: [Bool] = npy2.elements() 16 | XCTAssertEqual(e, elements) 17 | } 18 | 19 | func testFormatU1() { 20 | let elements = [UInt8.max, UInt8.min, 0, 0, 1, 2] 21 | let npy = Npy(shape: [3, 2], elements: elements, isFortranOrder: false) 22 | let data = npy.format() 23 | let npy2: Npy = try! Npy(data: data) 24 | 25 | XCTAssertEqual(npy.shape, npy2.shape) 26 | 27 | let e: [UInt8] = npy2.elements() 28 | XCTAssertEqual(e, elements) 29 | } 30 | 31 | func testFormatU2() { 32 | let elements = [UInt16.max, UInt16.min, 0, 0, 1, 2] 33 | let npy = Npy(shape: [3, 2], elements: elements, endian: .host, isFortranOrder: false) 34 | let data = npy.format() 35 | let npy2: Npy = try! Npy(data: data) 36 | 37 | XCTAssertEqual(npy.shape, npy2.shape) 38 | 39 | let e: [UInt16] = npy2.elements() 40 | XCTAssertEqual(e, elements) 41 | } 42 | 43 | func testFormatU4() { 44 | let elements = [UInt32.max, UInt32.min, 0, 0, 1, 2] 45 | let npy = Npy(shape: [3, 2], elements: elements, endian: .little, isFortranOrder: false) 46 | let data = npy.format() 47 | let npy2: Npy = try! Npy(data: data) 48 | 49 | XCTAssertEqual(npy.shape, npy2.shape) 50 | 51 | let e: [UInt32] = npy2.elements() 52 | XCTAssertEqual(e, elements) 53 | } 54 | 55 | func testFormatU8() { 56 | let elements = [UInt64.max, UInt64.min, 0, 0, 1, 2] 57 | let npy = Npy(shape: [3, 2], elements: elements, endian: .big, isFortranOrder: false) 58 | let data = npy.format() 59 | let npy2: Npy = try! Npy(data: data) 60 | 61 | XCTAssertEqual(npy.shape, npy2.shape) 62 | 63 | let e: [UInt64] = npy2.elements() 64 | XCTAssertEqual(e, elements) 65 | } 66 | 67 | func testFormatI1() { 68 | let elements = [Int8.max, Int8.min, 0, 0, 1, 2] 69 | let npy = Npy(shape: [3, 2], elements: elements, isFortranOrder: false) 70 | let data = npy.format() 71 | let npy2: Npy = try! Npy(data: data) 72 | 73 | XCTAssertEqual(npy.shape, npy2.shape) 74 | 75 | let e: [Int8] = npy2.elements() 76 | XCTAssertEqual(e, elements) 77 | } 78 | 79 | func testFormatI2() { 80 | let elements = [Int16.max, Int16.min, 0, 0, 1, 2] 81 | let npy = Npy(shape: [3, 2], elements: elements, endian: .big, isFortranOrder: false) 82 | let data = npy.format() 83 | let npy2: Npy = try! Npy(data: data) 84 | 85 | XCTAssertEqual(npy.shape, npy2.shape) 86 | 87 | let e: [Int16] = npy2.elements() 88 | XCTAssertEqual(e, elements) 89 | } 90 | 91 | func testFormatI4() { 92 | let elements = [Int32.max, Int32.min, 0, 0, 1, 2] 93 | let npy = Npy(shape: [3, 2], elements: elements, endian: .host, isFortranOrder: false) 94 | let data = npy.format() 95 | let npy2: Npy = try! Npy(data: data) 96 | 97 | XCTAssertEqual(npy.shape, npy2.shape) 98 | 99 | let e: [Int32] = npy2.elements() 100 | XCTAssertEqual(e, elements) 101 | } 102 | 103 | func testFormatI8() { 104 | let elements = [Int64.max, Int64.min, 0, 0, 1, 2] 105 | let npy = Npy(shape: [3, 2], elements: elements, endian: .little, isFortranOrder: false) 106 | let data = npy.format() 107 | let npy2: Npy = try! Npy(data: data) 108 | 109 | XCTAssertEqual(npy.shape, npy2.shape) 110 | 111 | let e: [Int64] = npy2.elements() 112 | XCTAssertEqual(e, elements) 113 | } 114 | 115 | func testFormatF4() { 116 | let elements: [Float] = [-3, -2, -1, 0, 1, 2] 117 | let npy = Npy(shape: [3, 2], elements: elements, endian: .host, isFortranOrder: false) 118 | let data = npy.format() 119 | let npy2: Npy = try! Npy(data: data) 120 | 121 | XCTAssertEqual(npy.shape, npy2.shape) 122 | 123 | let e: [Float] = npy2.elements() 124 | XCTAssertEqual(e, elements) 125 | } 126 | 127 | func testFormatF8() { 128 | let elements: [Double] = [-3, -2, -1, 0, 1, 2] 129 | let shape = [3, 2] + [Int](repeating: 1, count: 65535) 130 | let npy = Npy(shape: shape, elements: elements, endian: .little, isFortranOrder: false) 131 | let data = npy.format() 132 | let npy2: Npy = try! Npy(data: data) 133 | 134 | XCTAssertEqual(npy.shape, npy2.shape) 135 | 136 | let e: [Double] = npy2.elements() 137 | XCTAssertEqual(e, elements) 138 | } 139 | 140 | static var allTests = [ 141 | ("testFormatB1", testFormatB1), 142 | ("testFormatU1", testFormatU1), 143 | ("testFormatU2", testFormatU2), 144 | ("testFormatU4", testFormatU4), 145 | ("testFormatU8", testFormatU8), 146 | ("testFormatI1", testFormatI1), 147 | ("testFormatI2", testFormatI2), 148 | ("testFormatI4", testFormatI4), 149 | ("testFormatI8", testFormatI8), 150 | ] 151 | } 152 | -------------------------------------------------------------------------------- /Tests/SwiftNpyTests/NpzLoaderTests.swift: -------------------------------------------------------------------------------- 1 | 2 | import XCTest 3 | import SwiftNpy 4 | 5 | class NpzLoaderTests: XCTestCase { 6 | func testLoadNpz() { 7 | let npzData: Npz = try! Npz(data: npz) 8 | 9 | XCTAssertEqual(Set(npzData.keys), ["a", "b"]) 10 | let a: Npy = npzData["a"]! 11 | XCTAssertEqual(a.shape, [3]) 12 | let aElements: [Int] = a.elements() 13 | XCTAssertEqual(aElements, [0, 1, 2]) 14 | let b: Npy = npzData["b"]! 15 | XCTAssertEqual(b.shape, [4]) 16 | let bElements: [Int] = b.elements() 17 | XCTAssertEqual(bElements, [0, 1, 2, 3]) 18 | } 19 | 20 | static var allTests = [ 21 | ("testLoadNpz", testLoadNpz), 22 | ] 23 | } 24 | 25 | 26 | let npz = Data(bytes: [ 27 | 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x75, 0x8c, 0x4a, 0x6f, 0xc5, 28 | 0xde, 0x4a, 0x68, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x61, 0x2e, 29 | 0x6e, 0x70, 0x79, 0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 30 | 0x65, 0x73, 0x63, 0x72, 0x27, 0x3a, 0x20, 0x27, 0x3c, 0x69, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 31 | 0x6f, 0x72, 0x74, 0x72, 0x61, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 32 | 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 33 | 0x33, 0x2c, 0x29, 0x2c, 0x20, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 34 | 0x20, 0x20, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 36 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x75, 0x8c, 0x4a, 0x3c, 0x37, 0x1f, 0x5d, 0x70, 0x00, 0x00, 37 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x2e, 0x6e, 0x70, 0x79, 0x93, 0x4e, 38 | 0x55, 0x4d, 0x50, 0x59, 0x01, 0x00, 0x46, 0x00, 0x7b, 0x27, 0x64, 0x65, 0x73, 0x63, 0x72, 0x27, 39 | 0x3a, 0x20, 0x27, 0x3c, 0x69, 0x38, 0x27, 0x2c, 0x20, 0x27, 0x66, 0x6f, 0x72, 0x74, 0x72, 0x61, 40 | 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x27, 0x3a, 0x20, 0x46, 0x61, 0x6c, 0x73, 0x65, 0x2c, 41 | 0x20, 0x27, 0x73, 0x68, 0x61, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x28, 0x34, 0x2c, 0x29, 0x2c, 0x20, 42 | 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x00, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 45 | 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x75, 0x8c, 0x4a, 0x6f, 0xc5, 46 | 0xde, 0x4a, 0x68, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x6e, 0x70, 48 | 0x79, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x75, 0x8c, 49 | 0x4a, 0x3c, 0x37, 0x1f, 0x5d, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x8b, 0x00, 0x00, 0x00, 0x62, 51 | 0x2e, 0x6e, 0x70, 0x79, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 52 | 0x66, 0x00, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00 53 | ]) 54 | -------------------------------------------------------------------------------- /Tests/SwiftNpyTests/NpzSaverTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftNpy 3 | 4 | class NpzSaverTests: XCTestCase { 5 | func testFormat() { 6 | let npz1: Npz = try! Npz(data: npz) 7 | 8 | let a1: Npy = npz1["a"]! 9 | let a1Elements: [Int] = a1.elements() 10 | let b1: Npy = npz1["b"]! 11 | let b1Elements: [Int] = b1.elements() 12 | 13 | let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + "test.npz") 14 | try! npz1.save(to: tempURL) 15 | 16 | let data = try! Data(contentsOf: tempURL) 17 | 18 | let npz2: Npz = try! Npz(data: data) 19 | 20 | XCTAssertEqual(Set(npz2.keys), Set(npz1.keys)) 21 | 22 | let a2: Npy = npz1["a"]! 23 | let a2Elements: [Int] = a2.elements() 24 | let b2: Npy = npz1["b"]! 25 | let b2Elements: [Int] = b2.elements() 26 | XCTAssertEqual(a2Elements, a1Elements) 27 | XCTAssertEqual(b2Elements, b1Elements) 28 | } 29 | 30 | static var allTests = [ 31 | ("testFormat", testFormat), 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SwiftNpy 2 | Save/Load NumPy array files in Swift 3 | 4 | ```swift 5 | let npy = try Npy(contentsOf: npyUrl) 6 | let shape = npy.shape 7 | let elements: [Float] = npy.elements() 8 | let isFortranOrder = npy.isFortranOrder 9 | try save(npy: npy, to: url) 10 | ``` 11 | 12 | ```swift 13 | let npz = try Npz(contentsOf: npzUrl) 14 | let npy = npz["name-of-array"] 15 | try npz.save(to: url) 16 | ``` 17 | 18 | ## Suppoted formats 19 | `npy`, `npz` files. 20 | 21 | ### Bool 22 | `Bool` 23 | 24 | ### UInt 25 | `UInt8`, `UInt16`, `UInt32`, `UInt64` 26 | They also can be read as `UInt` 27 | 28 | ### Int 29 | `Int8`, `Int16`, `Int32`, `Int64` 30 | They also can be read as `Int` 31 | 32 | ### Float, Double 33 | `Float`, `Double` 34 | 35 | ## License 36 | 37 | [The MIT License](https://github.com/qoncept/swift-npy/blob/master/LICENSE) 38 | --------------------------------------------------------------------------------