123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- //
- // Codable.swift
- // CS.BigInt
- //
- // Created by Károly Lőrentey on 2017-8-11.
- // Copyright © 2016-2017 Károly Lőrentey.
- //
- extension CS {
- // Little-endian to big-endian
- struct Units<Unit: FixedWidthInteger, Words: RandomAccessCollection>: RandomAccessCollection
- where Words.Element: FixedWidthInteger, Words.Index == Int {
- typealias Word = Words.Element
- let words: Words
- init(of type: Unit.Type, _ words: Words) {
- precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
- self.words = words
- }
- var count: Int { return (words.count * Word.bitWidth + Unit.bitWidth - 1) / Unit.bitWidth }
- var startIndex: Int { return 0 }
- var endIndex: Int { return count }
- subscript(_ index: Int) -> Unit {
- let index = count - 1 - index
- if Unit.bitWidth == Word.bitWidth {
- return Unit(words[index])
- }
- else if Unit.bitWidth > Word.bitWidth {
- let c = Unit.bitWidth / Word.bitWidth
- var unit: Unit = 0
- var j = 0
- for i in (c * index) ..< Swift.min(c * (index + 1), words.endIndex) {
- unit |= Unit(words[i]) << j
- j += Word.bitWidth
- }
- return unit
- }
- // Unit.bitWidth < Word.bitWidth
- let c = Word.bitWidth / Unit.bitWidth
- let i = index / c
- let j = index % c
- return Unit(truncatingIfNeeded: words[i] >> (j * Unit.bitWidth))
- }
- }
- }
- extension Array where Element: FixedWidthInteger {
- // Big-endian to little-endian
- init<Unit: FixedWidthInteger>(count: Int?, generator: () throws -> Unit?) rethrows {
- typealias Word = Element
- precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0)
- self = []
- if Unit.bitWidth == Word.bitWidth {
- if let count = count {
- self.reserveCapacity(count)
- }
- while let unit = try generator() {
- self.append(Word(unit))
- }
- }
- else if Unit.bitWidth > Word.bitWidth {
- let wordsPerUnit = Unit.bitWidth / Word.bitWidth
- if let count = count {
- self.reserveCapacity(count * wordsPerUnit)
- }
- while let unit = try generator() {
- var shift = Unit.bitWidth - Word.bitWidth
- while shift >= 0 {
- self.append(Word(truncatingIfNeeded: unit >> shift))
- shift -= Word.bitWidth
- }
- }
- }
- else {
- let unitsPerWord = Word.bitWidth / Unit.bitWidth
- if let count = count {
- self.reserveCapacity((count + unitsPerWord - 1) / unitsPerWord)
- }
- var word: Word = 0
- var c = 0
- while let unit = try generator() {
- word <<= Unit.bitWidth
- word |= Word(unit)
- c += Unit.bitWidth
- if c == Word.bitWidth {
- self.append(word)
- word = 0
- c = 0
- }
- }
- if c > 0 {
- self.append(word << c)
- var shifted: Word = 0
- for i in self.indices {
- let word = self[i]
- self[i] = shifted | (word >> c)
- shifted = word << (Word.bitWidth - c)
- }
- }
- }
- self.reverse()
- }
- }
- extension CS.BigInt: Codable {
- public init(from decoder: Decoder) throws {
- var container = try decoder.unkeyedContainer()
- // Decode sign
- let sign: CS.BigInt.Sign
- switch try container.decode(String.self) {
- case "+":
- sign = .plus
- case "-":
- sign = .minus
- default:
- throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath,
- debugDescription: "Invalid big integer sign"))
- }
- // Decode magnitude
- let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in
- guard !container.isAtEnd else { return nil }
- return try container.decode(UInt64.self)
- }
- let magnitude = CS.BigUInt(words: words)
- self.init(sign: sign, magnitude: magnitude)
- }
- public func encode(to encoder: Encoder) throws {
- var container = encoder.unkeyedContainer()
- try container.encode(sign == .plus ? "+" : "-")
- let units = CS.Units(of: UInt64.self, self.magnitude.words)
- if units.isEmpty {
- try container.encode(0 as UInt64)
- }
- else {
- try container.encode(contentsOf: units)
- }
- }
- }
- extension CS.BigUInt: Codable {
- public init(from decoder: Decoder) throws {
- let value = try CS.BigInt(from: decoder)
- guard value.sign == .plus else {
- throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
- debugDescription: "BigUInt cannot hold a negative value"))
- }
- self = value.magnitude
- }
- public func encode(to encoder: Encoder) throws {
- try CS.BigInt(sign: .plus, magnitude: self).encode(to: encoder)
- }
- }
|