dmonad / lib0

Monorepo of isomorphic utility functions
MIT License
363 stars 65 forks source link

Swift implementation #54

Closed holtwick closed 1 year ago

holtwick commented 1 year ago

Just in case somebody needs a starting point for decode/encoder in Swift here is some code. Since the variable encoding of integers is one of the trickiest parts of it, these are examples of this functionality. The rest should be pretty straight forward. Maybe there is some place for it somewhere...

import Foundation

func readVarInt(bytes: [UInt8], pos: Int = 0) -> Int {
  var bytesPos = pos
  var r = bytes[bytesPos]
  var num: Int = Int(r & 0b00111111)
  var mult: Int = 64
  let sign = (r & 0b01000000) > 0 ? -1 : 1
  //  print("r=\(r) num=\(num) c=\(Int(r & 0b1)) sign=\(sign)")
  if Int(r & 0b10000000) == 0 {
    return sign * num
  }
  while true {
    bytesPos += 1
    r = bytes[bytesPos]
    num = num + Int(r & 0b01111111) * mult
    mult *= 128
    if r < 0b10000000 {
      return sign * num
    }
  }
}

func readVarUInt(bytes: [UInt8], pos: Int = 0) -> UInt {
  var bytesPos = pos
  var num: UInt = 0
  var mult: UInt = 1
  while true {
    let r = bytes[bytesPos]
    bytesPos += 1
    num = num + UInt(r & 0b01111111) * mult
    mult *= 128
    if r < 0b10000000 {
      return num
    }
  }
}

/// Just the same, but with Data as argument
func readVarUInt(data: Data, pos: Int = 0) -> UInt {
  var bytesPos = pos
  var num: UInt = 0
  var mult: UInt = 1
  while true {
    let r = data[bytesPos]
    bytesPos += 1
    num = num + UInt(r & 0b01111111) * mult
    mult *= 128
    if r < 0b10000000 {
      return num
    }
  }
}

func writeVarUInt(_ value: UInt) -> Data {
  var bytes = Data()
  var num = value
  while num > 0b01111111 {
    bytes.append(0b10000000 | (0b01111111 & UInt8(truncatingIfNeeded: num)))
    num = num / 128
  }
  bytes.append(UInt8(0b01111111 & num))
  return bytes
}

This is a unit test:

import XCTest

extension Data {
  func hexEncodedString() -> String {
    return map { String(format: "%02hhx", $0) }.joined()
  }
}

class Lib0Test: XCTestCase {

  func testReadVarInt() throws {
    XCTAssertEqual(10, readVarInt(bytes: [10]))
    XCTAssertEqual(128, readVarInt(bytes: [0x80, 0x02]))
    XCTAssertEqual(-1, readVarInt(bytes: [0x41]))
    XCTAssertEqual(-691529286, readVarInt(bytes: [0xc6, 0x99, 0xbf, 0x93, 0x05]))
    XCTAssertEqual(691529286, readVarInt(bytes: [0x86, 0x99, 0xbf, 0x93, 0x05]))
  }

  func testReadVarUInt() throws {
    XCTAssertEqual(10, readVarUInt(bytes: [10]))
    XCTAssertEqual(127, readVarUInt(bytes: [0x7f]))
    XCTAssertEqual(256, readVarUInt(bytes: [0x80, 0x02]))
    XCTAssertEqual(691529286, readVarUInt(bytes: [0xc6, 0xcc, 0xdf, 0xc9, 0x02]))
  }

  func testReadVarUIntWithData() throws {
    XCTAssertEqual(10, readVarUInt(data: Data([10])))
    XCTAssertEqual(127, readVarUInt(data: Data([0x7f])))
    XCTAssertEqual(256, readVarUInt(data: Data([0x80, 0x02])))
    XCTAssertEqual(691529286, readVarUInt(data: Data([0xc6, 0xcc, 0xdf, 0xc9, 0x02])))
  }

  func testWriteVarUInt() throws {
    print("Data \(writeVarUInt(10).hexEncodedString())")
    XCTAssertEqual(writeVarUInt(10).hexEncodedString(), "0a")  
    XCTAssertEqual(writeVarUInt(127).hexEncodedString(), "7f")
    XCTAssertEqual(writeVarUInt(256).hexEncodedString(), "8002")
    XCTAssertEqual(writeVarUInt(691529286).hexEncodedString(), "c6ccdfc902")
  }

}
dmonad commented 1 year ago

Thanks @holtwick for sharing!

I'm currently cleanup up old tickets and I want to close this issue as well as it's not immediately actionable.

michaelmedellin commented 3 months ago

@holtwick wondering if you ever got around to implementing an encoder/decoder on Swift? Looking at doing something similar shortly.

holtwick commented 3 months ago

@michaelmedellin I will not implement encoder/decoder on Swift in the near future. There is currently no project where I need to use it. So go for it :) You may use my code if it is useful for you. Consider it being MIT licensed.

michaelmedellin commented 3 months ago

@holtwick thanks for the quick reply! appreciate the offer for using your code. I may just do so!