C/C++でポインタを使ったバッファアクセスを行うときに、頻繁に使用するのがポインタのインクリメントとデクリメントです。ポインタの加算・減算なので、参照位置を変更することができ、連続したバッファで使用して順次アクセスができます。
この記事では、C/C++のポインタのインクリメント・デクリメントを行うコードを、Swiftではどのように書き換えることができるかを解説します。
Cのコード例
ここでは次のようなCのコードを書き換えます。
#include <unistd.h> #include <stdio.h> #include <stdlib.h> void fillSequentialNumber(void *buf, int32_t len) { uint8_t *p = (uint8_t *)buf; uint8_t *end = p + len; for (; p != end; p++) { *p = (p - (uint8_t *)buf) % 255; } } void dumpBuf(void *buf, int32_t len) { uint8_t *p = (uint8_t *)buf; uint8_t *end = p + len; for (; p != end; p++) { printf("%02x ", *p); } printf("\n"); } int main(int argc, const char * argv[]) { uint8_t buf[16] = {0}; fillSequentialNumber(buf, 16); dumpBuf(buf, 16); return 0; }
このコードを実行すると、次のように出力されます。
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Swiftでのコード例
Swiftで書き換えたコード例が次のコードです。Swiftではインクリメントとデクリメントがなくなったのと、直接ポインタ操作ができないので、そこをインデックスで表現しています。
import Foundation func fillSequentialNumber(buffer: UnsafeMutableBufferPointer<UInt8>, length: Int) { var cur = buffer.startIndex let end = buffer.startIndex.advanced(by: length) while cur < end { buffer[cur] = UInt8((cur - buffer.startIndex) % 255) cur = cur.advanced(by: 1) } } func dump(buffer: UnsafeMutableBufferPointer<UInt8>, length: Int) { var cur = buffer.startIndex let end = buffer.startIndex.advanced(by: length) while cur < end { print(String(format: "%02x ", buffer[cur]), separator: "", terminator: "") cur = cur.advanced(by: 1) } print("") } var buf = [UInt8](repeating: 0, count: 16) let len = buf.count buf.withUnsafeMutableBufferPointer { (ptr) -> Void in fillSequentialNumber(buffer: ptr, length: len) dump(buffer: ptr, length: len) }
このコードを実行すると、次のように出力されます。
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
他のやり方もあるとは思うのですが、思想を近づけて見たコードです。
バッファ確保
Cのコードではスタックに16バイトの配列で確保しましたが、SwiftではUInt8
の配列で確保しました。Data
を使っても同じです。
バッファへのアクセス
UInt8
の配列のバッファにアクセスするには、withUnsafeMutableBufferPointer
メソッドを使います。バッファにアクセスするコードはクロージャーで渡すのですが、このときに[UInt8]
のメソッドを使えないので、先に長さは取得しておきます。
インクリメントとデクリメントのの置き換え
UnsafeMutableBUfferPointer
の要素にアクセスするにはインデックスを使います。インクリメントとデクリメントはインデックスに対して行います。
ここでは、インクリメントの代わりにadvanced(by:)
を使いましたが、次のように+=
演算子を使って書くこともできます。
cur += 1
まとめ
インデックスの位置変更でCのインクリメントやデクリメントと、同じような考え方のコードを書くことができます。ポインタを直接操作しているのとは違うので、どちらかというと、バッファの要素へのランダムアクセスに近いコードです。
それでも、Cのコードの考え方にも近いコードかなと思います。