Upgrade to Pro — share decks privately, control downloads, hide ads and more …

See Swift C

See Swift C

Everyone is talking about the great high-level capabilities Swift offers; but virtually no one talks about how to get down and dirty with POSIX/Darwin, malloc, free, and friends. This talk plunges you into the Swift world of pointers and bits!

This presentation was given at iOSoho on 2015/7/13

David Hoerl

July 14, 2015
Tweet

More Decks by David Hoerl

Other Decks in Programming

Transcript

  1. See Swift C It’s been a sea of change for

    me! David Hoerl (dhoerl at mac dot com)
  2. Swift Is Fantastic! Modern and Rich Functional programming idioms Elegant

    nil handling with optionals Code can be very explicit, or quite terse Imposes more structure than Objective C
  3. Swift Is Fantastic! Modern and Rich Functional programming idioms Elegant

    nil handling with optionals Code can be very explicit, or quite terse Imposes more structure than Objective C But soooooo many blogs to read every week!
  4. Swift Works Well With Both Objective C and C Construct

    a bridging header Virtually all Objective C code imports Xcode 6/7 support Objective C annotations: nullability, type and NS_REFINED_FOR_SWIFT Access a Raft of C functions: Darwin, Core Foundation, your functions
  5. Reference “Using Swift with Cocoa and Objective-C” “Interacting With C

    APIs” Chapter Chart with mappings between C types and Swift Pointers and buffers
  6. Swift “Switch” Statement Look similar to C C compilers typically

    generate a jump table for performance Think of Swift case statements as cascading “if” and “if else” - first ‘case’ is always evaluated Structure code for readability, rich case statements
  7. Structs Like C structure with a suite of functions that

    operate on it Memory usage similar to C (rounds up to “stride()”) Protection from stupid errors: - contants and variables (constants set with init()) - mutating functions must be so marked - Protocols!
  8. struct S { var a: Int32 = 0 var b:

    Int32 = 0 func total() -> Int32 { let c = a + b return c } } var s = S() s.a = 10 s.b = 1 print("Sum: \(s.total())") print("Sizeof(S): \(sizeof(S)) sizeOfValue(s): \(sizeofValue(s))") Sum: 11 Sizeof(S): 8 sizeOfValue(s): 8 Structs Not shown: “stride(s) gives you the memory usage within an array
  9. Arrays Value types Typed, like C: “var foo: [S] =

    []” Memory usage similar to C Very rich API Copying does NOT mean memory duplication!
  10. var sArray = [S](count: 1_000, repeatedValue: s) // tried other

    initializers, no change to size below print("SizeOfValue(sArray): \(sizeofValue(sArray))") // prints 8 // This will let the closure in the arg access the Array's contiguous storage through ubp: sArray.withUnsafeMutableBufferPointer { (inout ubp: UnsafeMutableBufferPointer<S>) -> Void in print(ubp) // UnsafeBufferPointer(start: 0x0000000100809e20, length: 1000) let numElementsInBuffer = ubp.count // A pointer to the first and last element of the array's buffer: let firstElementBytePtr = UnsafePointer<UInt8>(ubp.baseAddress) let secondElementBytePtr = UnsafePointer<UInt8>(ubp.baseAddress.successor()) let lastElementBytePtr = UnsafePointer<UInt8>(ubp.baseAddress.advancedBy(numElementsInBuffer - 1)) let bufferElementStride = firstElementBytePtr.distanceTo(secondElementBytePtr) print("Stride in buffer: \(bufferElementStride)") // prints 16 let bufferSize = firstElementBytePtr.distanceTo(lastElementBytePtr) + bufferElementStride print("Size of buffer in bytes: \(bufferSize)") // prints 16000 } Advanced Arrays From https://forums.developer.apple.com/thread/8726
  11. Pointers Mutating and non-mutating (similar to “const”) - UnsafePointer<Memory> -

    UnsafeMutablePointer<Memory> Pointers and “Buffer Pointers” - UnsafePointer<Int32> equivalent to “const int32_t *p” - UnsafeBufferPointer<Int32> equivalent to “const int32_t[size]”
  12. More Pointers UnsafePointer: access via subscripts UnsafeBufferPointer: access via -

    subscripts (you can retrieve the bounds) - for byte in myBufferPointer { …
  13. import Swift import Darwin //import Foundation do { let ptr

    = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) // CChar == Int8 ptr[0] = 72 ptr[1] = 111 ptr[2] = 119 ptr[3] = 100 ptr[4] = 105 ptr[5] = 101 ptr[6] = 33 if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Simple UnsafeMutablePointer
  14. import Swift import Darwin //import Foundation do { let ptr

    = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) // CChar == Int8 ptr[0] = 72 ptr[1] = 111 ptr[2] = 119 ptr[3] = 100 ptr[4] = 105 ptr[5] = 101 ptr[6] = 33 if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Simple UnsafeMutablePointer
  15. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  16. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  17. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  18. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  19. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  20. do { let ptr = UnsafeMutablePointer<CChar>(calloc(7 + 1, 1)) for

    (i, c): (Int, Character) in "Howdie!".characters.enumerate() { ptr[i] = String(c).nulTerminatedUTF8.withUnsafeBufferPointer( { (p: UnsafeBufferPointer<UInt8>) -> CChar in return CChar(p[0]) } ) } if let newString = String.fromCString(ptr) { print(newString) } free(ptr) } Dissecting Strings
  21. func hexToData0(str: NSString) -> NSData { let len = str.length/2

    var data = NSMutableData(capacity:len)! var num: [Int8] = [ 0, 0, 0 ] let ptr = str.cStringUsingEncoding(NSUTF8StringEncoding) for var i=0; i<len; ++i { num[0] = ptr[i*2+0] num[1] = ptr[i*2+1] var n = UInt8 ( strtol(&num, nil, 16) ) data.appendBytes(&n, length:1) } return data; } - (NSData *)hexToData:(NSString *)str { const char *ptr = [str cStringUsingEncoding:NSASCIIStringEncoding]; NSUInteger len = [str length]/2; NSMutableData *data = [NSMutableData dataWithCapacity:len]; while(len--) { char num[3] = (char[]){ 0, 0, 0 }; num[0] = *ptr++; num[1] = *ptr++; uint8_t n = (uint8_t)strtol(num, NULL, 16); [data appendBytes:&n length:1]; } return data; } Simple C ➡ Swift http://stackoverflow.com/questions/26346542/how-to-translate-push-tokens-between-nsdata-and-nsstring-representations-in-swif/26346543
  22. - (NSData *)hexToData:(NSString *)str { const char *ptr = [str

    cStringUsingEncoding:NSASCIIStringEncoding]; NSUInteger len = [str length]/2; NSMutableData *data = [NSMutableData dataWithCapacity:len]; while(len--) { char num[3] = (char[]){ 0, 0, 0 }; num[0] = *ptr++; num[1] = *ptr++; uint8_t n = (uint8_t)strtol(num, NULL, 16); [data appendBytes:&n length:1]; } return data; } Simple C ➡ Swift http://stackoverflow.com/questions/26346542/how-to-translate-push-tokens-between-nsdata-and-nsstring-representations-in-swif/26346543 func hexToData0(str: NSString) -> NSData { let len = str.length/2 var data = NSMutableData(capacity:len)! var num: [Int8] = [ 0, 0, 0 ] let ptr = str.cStringUsingEncoding(NSUTF8StringEncoding) for var i=0; i<len; ++i { num[0] = ptr[i*2+0] num[1] = ptr[i*2+1] var n = UInt8 ( strtol(&num, nil, 16) ) data.appendBytes(&n, length:1) } return data; }
  23. - (NSData *)hexToData:(NSString *)str { const char *ptr = [str

    cStringUsingEncoding:NSASCIIStringEncoding]; NSUInteger len = [str length]/2; NSMutableData *data = [NSMutableData dataWithCapacity:len]; while(len--) { char num[3] = (char[]){ 0, 0, 0 }; num[0] = *ptr++; num[1] = *ptr++; uint8_t n = (uint8_t)strtol(num, NULL, 16); [data appendBytes:&n length:1]; } return data; } Simple C ➡ Swift http://stackoverflow.com/questions/26346542/how-to-translate-push-tokens-between-nsdata-and-nsstring-representations-in-swif/26346543 func hexToData0(str: NSString) -> NSData { let len = str.length/2 var data = NSMutableData(capacity:len)! var num: [Int8] = [ 0, 0, 0 ] let ptr = str.cStringUsingEncoding(NSUTF8StringEncoding) for var i=0; i<len; ++i { num[0] = ptr[i*2+0] num[1] = ptr[i*2+1] var n = UInt8 ( strtol(&num, nil, 16) ) data.appendBytes(&n, length:1) } return data; }
  24. - (NSData *)hexToData:(NSString *)str { const char *ptr = [str

    cStringUsingEncoding:NSASCIIStringEncoding]; NSUInteger len = [str length]/2; NSMutableData *data = [NSMutableData dataWithCapacity:len]; while(len--) { char num[3] = (char[]){ 0, 0, 0 }; num[0] = *ptr++; num[1] = *ptr++; uint8_t n = (uint8_t)strtol(num, NULL, 16); [data appendBytes:&n length:1]; } return data; } Simple C ➡ Swift http://stackoverflow.com/questions/26346542/how-to-translate-push-tokens-between-nsdata-and-nsstring-representations-in-swif/26346543 func hexToData0(str: NSString) -> NSData { let len = str.length/2 var data = NSMutableData(capacity:len)! var num: [Int8] = [ 0, 0, 0 ] let ptr = str.cStringUsingEncoding(NSUTF8StringEncoding) for var i=0; i<len; ++i { num[0] = ptr[i*2+0] num[1] = ptr[i*2+1] var n = UInt8 ( strtol(&num, nil, 16) ) data.appendBytes(&n, length:1) } return data; }
  25. CSV Parsers CHCSVParser: read & write, huge suite of options


    https:/ /github.com/davedelong/CHCSVParser cCSVParse: small C function, returns a dictionary
 Original: http:/ /michael.stapelberg.de/cCSVParse/
 Supported: https:/ /github.com/JanX2/cCSVParse CSVParserInSwift
 https:/ /github.com/dhoerl/CSVParserInSwift
  26. CSV Character Loop var textp, laststop, lineEnd: UnsafePointer<CChar> while textp

    < lineEnd { switch textp[0] { case ASCII.BackSlash.rawValue: textp++ case ASCII.quote.rawValue: quoteCount++ case delimiter where quoteCount % 2 == 0: // There is a delimiter which is not between an unmatched pair of quotes? if textp > laststop { let s = parseStringFrom(laststop, to: textp) if !s.isEmpty { delegate?.csvParserDidReadField(s, atIndex: fieldNumber) } } ++fieldNumber laststop = textp + 1 default: break } // Go to the next character textp++ } const char *textp, *laststop, *lineEnd; while (textp < lineEnd) { switch(textp[0]) { case '\\': textp++; break; case '\"': quoteCount++; break; default: if(textp[0] == delimiter && (quoteCount % 2) == 0) { // There is a delimiter which is not between an unmachted pair of quotes? if(textp > laststop) { NSString *s = parseString(textp, laststop, encoding); if([s length] > 0 && respondsToDidReadField) { [delegate parser:self didReadField:s atIndex:fieldNumber]; } } ++fieldNumber; laststop = textp + 1; } } // Go to the next character textp++; }
  27. CSV Character Loop const char *textp, *laststop, *lineEnd; while (textp

    < lineEnd) { switch(textp[0]) { case '\\': textp++; break; case '\"': quoteCount++; break; default: if(textp[0] == delimiter && (quoteCount % 2) == 0) { // There is a delimiter which is not between an unmachted pair of quotes? if(textp > laststop) { NSString *s = parseString(textp, laststop, encoding); if([s length] > 0 && respondsToDidReadField) { [delegate parser:self didReadField:s atIndex:fieldNumber]; } } ++fieldNumber; laststop = textp + 1; } } // Go to the next character textp++; } var textp, laststop, lineEnd: UnsafePointer<CChar> while textp < lineEnd { switch textp[0] { case ASCII.BackSlash.rawValue: textp++ case ASCII.quote.rawValue: quoteCount++ case delimiter where quoteCount % 2 == 0: // There is a delimiter which is not between an unmatched pair of quotes? if textp > laststop { let s = parseStringFrom(laststop, to: textp) if !s.isEmpty { delegate?.csvParserDidReadField(s, atIndex: fieldNumber) } } ++fieldNumber laststop = textp + 1 default: break } // Go to the next character textp++ }
  28. My Favorite Swift Blogs Erica Sadun: http:/ /ericasadun.com
 (lots on

    Playgrounds, lively, this new mother never sleeps!) Airspeed Velocity: http:/ /airspeedvelocity.net
 (really high level, mind twisters) Natasha: http:/ /natashatherobot.com Jameson Quave: http:/ /jamesonquave.com/blog NSHipster: http:/ /nshipster.com (well known) Mike Ash: https:/ /www.mikeash.com/pyblog/ (well known)
  29. Related Forum Posts Memory Layout: 
 https:/ /devforums.apple.com/thread/272013 Memory consumed

    by large arrays:
 https:/ /forums.developer.apple.com/thread/8726