Skip to content

Instantly share code, notes, and snippets.

@sidepelican
Created May 2, 2020 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sidepelican/f8b69831d0a893d7f93eb116fb5162f1 to your computer and use it in GitHub Desktop.
Save sidepelican/f8b69831d0a893d7f93eb116fb5162f1 to your computer and use it in GitHub Desktop.
uses libiconv.2.tbd, swift5.2
import Darwin.POSIX.iconv
class Iconv {
enum Error: Swift.Error {
case illegalByteSequence
case invalidArgument
case invalidResult
case unknownError(errno: Int32)
}
private let context: iconv_t
init(from fromCode: String) {
context = iconv_open("UTF-8", fromCode)
}
deinit {
iconv_close(context)
}
func decode(_ data: Data, tmpBufferSize: Int = 2048) throws -> String {
try data.withUnsafeBytes { dataPtr -> String in
var rawResultBuffer: [Int8] = []
let tmpBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: tmpBufferSize)
defer {
tmpBuffer.deallocate()
}
var inBuffer = UnsafeMutablePointer(mutating: dataPtr.baseAddress?.assumingMemoryBound(to: Int8.self))
var inBytesLeft = dataPtr.count
while inBytesLeft > 0 {
var outBuffer: UnsafeMutablePointer<Int8>? = tmpBuffer
var outBytesLeft = tmpBufferSize
if iconv(context, &inBuffer, &inBytesLeft, &outBuffer, &outBytesLeft) == -1 {
switch errno {
case EILSEQ:
throw Error.illegalByteSequence
case E2BIG:
break
case EINVAL:
throw Error.invalidArgument
default:
throw Error.unknownError(errno: errno)
}
}
rawResultBuffer.append(contentsOf: UnsafeMutableBufferPointer(start: tmpBuffer, count: tmpBufferSize - outBytesLeft))
}
guard let result = String(utf8String: &rawResultBuffer) else {
throw Error.invalidResult
}
return result
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment