Title

iOSでSMBライブラリを実装する(その5)ホストの検出(ブラウジング)

Windowsでマイネットワークを開くと近くのコンピュータが自動的に表示されます。
これは単純にブロードキャストしてそれぞれが応答をするのではなく、バックアップブラウザを持っているホストに問い合わせをすることで実現しています。

バックアップブラウザはLAN上のコンピュータが自動的に選定されて、どれかがなります。
OSのバージョンによって優先度が決まっていたりしますが、問い合わせるだけなのでその部分はあまり気にしないことにします。

さて、これを実装しようとすると結構面倒で、

1.バックアップブラウザの検索
2.バックアップブラウザからブラウザリスト(ホスト一覧)の取得
と2段階を踏む必要があります。

1はブロードキャストで問い合わせ(GetBackup Request)をすると、バックアップブラウザが応答(GetBackup Response)を返してくれます。
ここで一つ重大な問題があります。

問い合わせは当然ながらIPで行っているわけですが、元はNetBIOSのため、バックアップブラウザが自分に対して応答を返すときに、名前解決を要求してきます。
よってNBNSの問い合わせに応答できるように実装しておかなければなりません。
(IPで送ってるんだからそのアドレスに返してよ・・と思いますがそうはなっていません)

これは本来であればOSの機能なので、アプリで実装してしまうと、他のアプリと同時にポートをBINDしたときに問題が起こりそうですね。

次に2ですが、これはSMBと同じです。
しかし、ダイレクトホスティング(ポート445番)では挙動が不安定でした(Windows7で確認)。
ちゃんとNetBIOSセッションサービスを利用すると安定して取得できます。謎です。

さて、この方法ですと、ワークグループ名の指定が必要です。
また何かしらの理由でバックアップブラウザが存在しないとホスト一覧を取得できません。

以下は禁じ手だと思うのですが、GoodReaderの実装がそうなっていたので紹介しておきます。
確実な方法ですが、ポートスキャンがちょっと攻撃っぽいので今のところComicGlassでは非採用にしました。

1.ネットワークのサブネットマスクからネットワークアドレスとホストアドレスを分離します。
2.全てのホストアドレスのTCP/445番ポートに対して接続を試みます。(つまりポートスキャン)
3.接続できたら、セッションセットアップの途中までやります。途中というのは相手のホスト名が判明するところまでです。(認証の途中)
4.ホスト名(NetBIOS名)が取得できたら切断しちゃいます。

とりあえず上記の方法であればブラウジング機能に頼らずホストを検出できます。ただし同じサブネットの中のみ。
(ポートスキャンしているから当然といえば当然ですが・・・)

iOSでSMBライブラリを実装する(その4)~あると便利なもの

今回はただの愚痴ですが・・・。

SMB/CIFSの仕様をみると、データの途中にいきなり可変長データが入っていたりします。
さらにpaddingがいたるところに出てきます。

また、変数は存在するものの、実際のところどういう値にしていいのか、定数としてどんなものがあるのかわからないことが多々あります。

そんなときはプロトコルアナライザです。
WindowsServer系ならばリソースキットで利用できますが、WireSharkなんかも便利です。

そして、絶対あったほうが良いもの・・。

それはポートミラーリング、またはポートモニタリング機能がついたスイッチングハブです。
完全なリピータハブがあればそれでもいいですが、おそらく10BASE-Tの時代のものしか無いと思うので・・・。

数年前までは何万円もしましたが、今では1万円を切る商品もあるみたいです。

iOS向けSMBライブラリを実装する(その3)~NetBIOS名の解決

Windowsで使われる、NetBIOSまわりを頑張ってみます。

やりたいことは、Windowsコンピュータ名(NETBIOS名)からIPアドレスを取得することです。

NetBIOSはとにかく不可解です。
本当に不可解なんですが、歴史的背景があるので仕方がありません。
どのくらい古いかというと1980年代くらいなので仕様書もイマイチ読みづらく、後の改変も多数あるので本当にわかりづりです。

まずNetBIOSはTCP/IPとは別物です。
現在ではプロトコル部分をNetBEUI、インターフェイス部分をNetBIOSと呼ぶようですが、元はインターフェイス(API)からプロトコルまですべてひっくるめた規格でした。

NetBEUIは現在では使われることは無くなりましたが、NetBIOSのインターフェイスだけは利用され続けているというわけです。
NetBIOSをTCP/IPで利用できるようにしたものがNetBIOS over TCP/IPです。(TCP/IPとか言いながらUDP/IPも多用します)
これ時代もWindows3.x時代のものなので何かと理解しづらいです。

さて、関係ありそうな、NetBIOS over TCP (NBT) Extensions NetBIOS over TCP (NBT) Extensions[MS-NBTE]をまず見てみると
名前解決についてはRFC1002を見るように書いてあります。

RFC1002の内容は「NetBIOS-over-TCP」となっています。
日付は March 1987・・・。
読んでみると・・・なんか意味不明・・ですががんばって読む。

とりあえずパケットのフォーマットはこう

                       1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         NAME_TRN_ID           | OPCODE  |   NM_FLAGS  | RCODE |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          QDCOUNT              |           ANCOUNT             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          NSCOUNT              |           ARCOUNT             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

で、名前解決のリクエストはこういう固定値を入れる

4.2.12.  NAME QUERY REQUEST

                        1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         NAME_TRN_ID           |0|  0x0  |0|0|1|0|0 0|B|  0x0  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          0x0001               |           0x0000              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          0x0000               |           0x0000              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   /                         QUESTION_NAME                         /
   /                                                               /
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           NB (0x0020)         |        IN (0x0001)            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Bはブロードキャストフラグなので、1で固定みたいです。
なのでフラグ部分は0x0110。

QUESTION_NAMEの部分はRFC 883を使ってコード化しろって書いてます! 
これがまだ何故??というやり方で頭に???浮かびっぱなしです。

とりあえずNETBIOS名は15文字です。本来は16文字でしたが、最後の1バイトを別の用途に割り当てたため15文字。
15文字に満たない場合はスペース(0x20)で埋めます。

16文字目はサフィックスで種別を表すのですが、
0x00だとワークステーション サービス
0x20だとファイル サーバー サービス
0x1bだとドメイン マスタ ブラウザ
・・・などなどを表すようですが、、
これはもともとのLAN Managerの仕様ではなくMicrosoftの拡張仕様です。
どうやら0x00はクライアント、0x20がサーバサービスを示すようなのでここでは0x20が正解だと思われます。(実際にはどちらも名前登録されていて、反応もしてくれるので良いのですが)

で、肝心のNETBIOS名の格納方法です。
最初の1バイトは0x20を指定します。0x20=長さが32byteの意味で、NETBIOS名の場合は32byte固定と書いてあるので必ず0x20を指定。

NEBIOS名+サフィックスの16byteを以下の手順で符号化して32Byteにします。
最後にNULL(0x00)くっつけるので、長さを表す先頭の0x20と合わせて34byteになります。
これがQUESTION_NAMEになります。

NETBIOS名の符号化は、
まず文字コードを4バイト単位にわけます。
空白文字(0x20)であれば0x2と0x0です。
0x0をA、0x1をB、0x2をC・・・という感じで文字を割り当てます。
0x20であれば「CA」になります。

つまりNETBIOS名が「FRED」であれば「EGFCEFEECACACACACACACACACACACACA」になります。

さて、これをUDPでブロードキャストすれば該当ホストがレスポンスを返してくれます。
iOSで1024番以下のポートをbindできるのか?とちょっと不安でしたが、やってみたら大丈夫でした。

レスポンスの内容は以下のとおり

4.2.13.  POSITIVE NAME QUERY RESPONSE

                        1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         NAME_TRN_ID           |1|  0x0  |1|T|1|?|0 0|0|  0x0  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          0x0000               |           0x0001              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          0x0000               |           0x0000              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   /                            RR_NAME                            /
   /                                                               /
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           NB (0x0020)         |         IN (0x0001)           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TTL                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           RDLENGTH            |                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
   |                                                               |
   /                       ADDR_ENTRY ARRAY                        /
   /                                                               /
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TTLより前はリクエストと同じ。
ADDR_ENTRY ARRAYにIPアドレスが入ります。
(複数ある場合は複数入ります。パケットの送信アドレスを見るか、自分のネットワークマスクと較べてどれを利用するか決めます)

実際には名前の解放パケットとかも飛んでくるようですが(それを見てキャッシュから削除する)、iOSでそれを監視し続けるのは非現実的なので無視します。

なお、IPv6の場合はLLMNR(Link-Local Multicast Name Resolution)を使う必要があります。
今回はIPv4だけでいいのでそっちはやりません。
(そのくらいの知識がある人はIPアドレスでわかるでしょうし・・・)

さて、これでコンピュータ名(NETBIOS名)を解決できるようになりました。
まぁ、なったところでどう使うかは悩ましいところなんですけどね。

iOSでSMBライブラリを実装してみる(その2)~NTLM認証

Windows関連情報は運用を目的にしたものが多く、コーディング例はあんまり聞かないのでメモ的に書いておきます。

SMBの認証にはLM認証、NTLMv1認証、NTLMv2認証、Kerberos認証のいずれかを使います。
Kerberos認証は鍵交換でActiveDirectly環境想定のものなので除外。

LM認証はNTLMv1認証はだいぶ古いにもかかわらずつい最近まで使われていましたがWindows7からはデフォルト無効になっているようなのでこちらも使わない方針で行きます。
ようするに面倒なのでNTLMv2だけ対応します。
あと、まず非対応ということはあり得ないので、Unicodeだけ対応します。

さて、NTLMは「NT LAN Manager Authentication Protocol」のことらしいです。
Microsoftの仕様書名は[MS-NLMP]です。

基本的には認証情報をバイト列に並べているだけなので仕様書を読みながら実装すればなんとか出来ます。
SMB/CIFSがアライメントやら謎のpaddingやらとても実装しにくいのに比べると可変長データをpayloadにまとめているので少しだけ気楽です。

クライアント側で生成する必要があるのは、NEGOTIATE_MESSAGEとAUTHENTICATE_MESSAGEです。
前者は本当に必要な情報を連結してバイト列にしているだけなので楽勝。それでも実際実装してみると一発ではなかなか思い通りにならないのですが・・・。

後者のAUTHENTICATE_MESSAGEもほとんど同じですが、NEGOTIATE_MESSAGEに対してサーバから返されたCHALLENGE_MESSAGEに含まれる情報を使ってチャレンジレスポンスを計算する必要があります。
それがNTLMv2なわけですね。
AUTHENTICATE_MESSAGEのうち、計算が必要なのはNTLMv2 Responseというフィールドです。

といっても、使うのはmd4とmd5だけなんですね。
試行錯誤の結果正しい結果を出せるようになったのでメモ。

入力
・ドメイン(大文字のみ,UTF16LittileEndian)
・ユーザ名(大文字のみ,UTF16LittileEndian)
・パスワード(UTF16LittileEndian)
※ドメインとユーザー名は全て大文字に変換しておく
※文字列のNULLターミネーションは不要(含めない)

サーバから(CHALLENGE_MESSAGEの内容)
・Server challenge (8byte)
・TargetInfo (可変長)

(クライアント側で)生成するもの
・タイムスタンプ(8byte)
・Client challenge (8byte乱数)

1.パスワードのmd4ハッシュを求める
2.ユーザ名とドメイン名を連結する(例:「user」+[Domain]→「USERDOMAIN」 )
※ここを先入観でDOMAIN+USERの順番だと思い込んでハマりました・・・
3.2に対して、1を鍵として、hmac_md5を計算する
4.以下を連結する(これをblobと言うらしい)
  署名(固定値=0x01010000)
  4byteの予約領域その1(0x00000000)
  タイムスタンプ(8byte)
  Client challenge(8byte)
  4byteの予約領域その2(0x00000000)
  TargetInfo
  4byteの予約領域その3(0x00000000)
5.Server challengeと4を連結する
6.5に対して、3を鍵として、hmac_md5を計算する
7.6と4を連結する

7で出来たものがNTLMv2レスポンスになります。
ややこしいですね。

MD4はRFC 1320
MD5はRFC 1321
HMACは RFC 2104に規定されています。
なお、それぞれC言語コードが附属しています。
たいして計算量も無いので最適化も不要でしょうから、そのまま使ってもOKかと思います。ただしライセンス表記が必要です。
探せばそのあたりをクリアしたコードも見つかります。
(僕はmd4はそのまま使いました。md5は以前準備したコードがあったのでそっちを利用)

基本的に、SMBと同じく全てリトルエンディアンが使われます。
普通ネットワークバイトオーダーと言えばビッグエンディアンなんですけどね。
最初はエンディアン気にしてコード書いてましたが、どうせiPhoneもリトルエンディアンだし途中から面倒になってリトルエンディアン環境想定で作りました。

さて、これでSMBサーバと認証ができるようになりました。

次回はWindowsファイル共有に使うNETBIOS名(ホスト名)をNetBIOS over TCP/IPを使って名前解決(IPアドレスを取得する)をやってみたいと思います。
まぁ、IPアドレスでアクセスすれば良いので不要な気もしますが。。

欲を言えばBROWSERプロトコルを使ってネットワーク上のNETBIOSホスト名を自動収集したいところですが、、まぁNASとかはBonjourに対応してるから不要な気もします。

2015-05-18 修正 challengeのスペルが違うというお恥ずかしいミスがありましたのでこっそり修正。

夜更かししないためのライフハック

昔実践していたことがあるのが、ひとりサマータイムもどき。
自分だけ時計を1時間か2時間ずらして生活します。(時計を進めます)

そうすると時計が午前2時とか3時とかになるまで起きていても平気です。(気分ですよ気分)
実際には午前0時頃なわけですが、さすがに(表示が)1時、2時になると寝ようかなーという気分になるわけです。

これはこれでよいのですが、昼間は他の人と時間がずれていて何かと不便です。

といわけで、帳尻合わせて夜だけ時計を進める歪みタイムゾーンを考えてみました。

・午前6時から午後6時までは普通の時計と同じです。
・午後6時からちょっと加速して、午後10時になるころに表示は24時(0時)になります。
・午後10時から午前2時までは表示は2時間進んだまま、通常と同じ速度で表示が進みます。
・午前2時から午前6時の間に2時間の進みを帳尻合わせします。

<図解>

とりあえずみんな笑顔でハッピーということで「スマイルタイム」と名づけました。
(というかいろいろ検索したんだけどわかりやすくて取れるドメインがこれでした)

http://smiletime.info/ <スマイルタイム>

↑思いついたらすぐになんかしてみたくなるので、説明のためのサイト作りました。全てパブリックドメイン的に扱って構わないので是非お試しください。普及活動します(弱々しく)
 寝る時間だけではなく、仕事もはやく終えるようになったらいいですよね。
 iPhone時計アプリとかもそのうち作ってみる。

iOS上でSMB/CIFS(Windowsファイル共有)をゼロから実装してみる

何故か全くライブラリが見つからないSMB/CIFS。
ググってみても、「SMBのiOSライブラリないの?(そもそもObj-cに限定しなくても無い)」「sambaがGPL、ぐぬぬぬ・・・」という記事しか見当たらない。組込み向けの製品はいくつか見つかりましたが、たぶん個人で買えるようなものじゃない。
きっと実装は厄介なんだろうなと見る気もしてなかったですが、風邪でダウンしていたので、その時に一度気合を入れてテストしてみました。

なお、tangoというOSSがあるようですが、うまく動かない。
恐らく認証関連が古いせいで最近のWindowsでは動かないぽい。
extended security flagが立っていると即座にエラー返すようなのできっとダメだろう、ってことでtangoは諦める。

SMBはSMB1とSMB2があって、SMB2は大きなバッファが扱えて高速LANで効率がいいようですが、NASとかで対応していないものもあるかもしれないし、iPhoneのWifiからではほとんど速度は変わらない(と思う)のでまずSMB1にトライ。(SMB1の仕様書読んでいるうちに、ファイルオフセット指定が32bitしかなく、2G超えるファイルダメじゃん!って気づいたけど気づかなかったことにします:→追記で訂正)

悪名高いMicrosoftの仕様書を読み解くわけですが、SMBは魑魅魍魎な方言やら実はこうでしたー! 残念! みたいな実装があって苦しむと噂には聞いております。

さて、さて、
仕様書読むと案外単純な通信です。
これはいける。

流れは、
・ネゴシエーション
・セッションリクエスト(認証)
・ツリー接続、一覧取得、(ここでパイプ、RPCの仕様書を読まされる(涙))
・ファイル一覧の取得 (ワイルドカード*でFindFileする。ルートディレクトリだと失敗するのでちょっと変える必要ある
・データの転送(実は簡単な命令っぽい)

という感じで、バイナリデータなのでC言語では扱いやすい(はず・・)

しかしセッションリクエストで早速ハマる。

[MS-CIFS]306ページ、2.2.4.53 SMB_COM_SESSION_SETUP_ANDX (0x73)

This command was introduced in the LAN Manager 1.0 dialect.
The formats of the request and response messages have changed since the command was first defined. The CIFS format, as defined for the NT LAN Manager dialect, is presented here. This format MUST be used when the NT LAN Manager dialect has been negotiated.
(後略)

このコマンドはLAN Manager1.0のときの(方言で?)定義された。
このコマンドが最初に定義されてから、リクエストと応答メッセージのフォーマットが変わったよ。

うーん。LAN Manager1.0はIBMの頃の仕様でもう使われていないので、NT LMを使うみたいね。
Wordデータは26 bytesなので、WordCoundは13になるはず??? よくわからん

他の通信をパケットキャプチャしてみるとWordCountが12=24Byteしかない。1バイトどこいったー?

試しにきにせず26Byteで送ってみたら見事エラーでございます・・・。

と思ったら別のファイルの[MS-SMB]の方の53ページにextended security flagを使う時にはSecurityBlobLengthを使えって書いてあった。・・・といった具合に仕様書読むにもコツが必要な模様。

とりあえずWindows7とNAS相手にツリー一覧取得までは出来たので、頑張ればなんとかなるかなー。
どうも一番面倒なのはツリー一覧取得するところだったっぽい。
もう少しちゃんとエラー処理とか、ネゴシエーションやらないと実用的には使えないと思うので作業量は多そうですが。

時間かかる要素その2は、SMB通信に使うNTLMの暗号化をどうするか。
とりあえず外部ライブラリ使って認証が通ることは確認しましたが、ここも自前で実装するとなると読まなければならない仕様書がまた増えて面倒くさい。
まぁ、こちらの方が内容はある程度クリアなので実装することは出来そうですが、まっとうにやったら時間かかりそうです。まっとうじゃない方法探します。

NTLMもUIWebViewでは使えるので、API公開してくれたらいいのに・・・。

ComicGlassへはうまく出来たら実装します。

追記:

だいぶ分かってきました。
上記記述にいくつか間違いがあったので一応訂正。

ファイルのリードについては見ていた資料が古かったようで、新しい資料にはOffsetの上位バイトのフィールドがありました。
(SMB_COM_READ_ANDX==0xeE最後の4バイト,Optinal)
よって2GBを超えるファイルもオフセット付で扱えます。
またTimeoutがTimeout_or_MaxCountHighになっており、ファイルを読む場合はMaxCountHighとして扱うように変わってました。
ネゴシエーションの時に対応非対応を設定できるようです。
(しかしWindowsXPの通信見てみると使ってない・・・)

ときおり出てくるDataOffsetとかの意味がわかって受信データのpaddingサイズ不要が要らないことがわかりました。(DataOffsetでデータの先頭がわかる)

フィリピン災害への寄付

【寄付ハック】フィリピン災害へ寄付した人に、オレが自腹でiPad miniプレゼントキャンペーン(fladdictさん)

fladdictさんはいつもアイデア素晴らしい。
全員が得するやり方でないと支持されにくいんですよね。うまい。
まんまと宣伝しちゃうぜ!

僕も応募したよー

添い寝アラームアップデート 1週間無料にします!

添い寝アラームは子供寝かしつけていたら自分も寝てしまって気づいたら夜中/朝だよ!
洗濯してないし、洗い物もそのままだよ!
大人の時間が! 
深夜アニメ見るつもりだったのに!

といったような事を防ぐ、小さいお子さんをお持ちのかたのためのアプリです。

使い方はアラームスタートボタンをタップするだけ。
あとは画面を下に伏せておいておけば、マイクと加速度センサをつかって、良い頃合いに起こしてくれます。
アラームは最初はバイブレータ、次は音。止めないと音がだんだん大きくなります。

アラームは本体を叩くと延長されます。画面見なくても暗闇でスヌーズできます。

今回のアップデートでiOS7及びiPhone5s/5cに最適化しました。
(前のバージョンでも動いていましたが)

アイデアも含めけっこう自信作、、というか自分ではとても役立ってるアプリなので是非多くのパパママに使って頂きたく、1週間無料にしておきますのでパパママお試し下さい!

よろしくおねがいします。

【高速化】abs()関数を使うべきか

絶対値を求めるC言語の関数はabs() です。

なんとなく関数呼び出しのコストがもったいない気がして、

int a = b >0 ? b : -b;

みたいな書き方を見かけることがあります。
(というか意識せずに自分でも書いていることが・・・)

ちょっと話は変わって、絶対値計算に条件分岐が必要だと思いますか?

答えは環境依存、、ということになりますが、大抵の環境では条件分岐不要です。
現代の多くの環境においてはabs()関数を使えば関数呼び出しはインライン展開された上に分岐なしの絶対値計算コードが出力されます。
だからあまり気にせずabs()を使ってOK。

VisualC++にてint a = abs(b);というプログラムをコンパイルした結果は以下のような感じになりました。たったの3命令、しかも分岐なし。

(EAXレジスタにbの値が入っている)

cdq 
xor eax,edx 
sub eax,edx

CDQが見慣れない感じですが、CDQ命令はEAXレジスタの符号をEDXレジスタのすべてのビット位置にコピーする便利命令です。

C言語風に書くと以下のような感じでしょうか。

a = (b ^ (b >> 31)) - (b >> 31);

結論。
標準関数は自分で再実装せず、ありがたくそのまま使え。

Objective-Cで中国語文字コードのC文字列をNSStringに読み込む

これわからずに非常に苦労しました。
そもそも中国語の文字コードなんて知らんし・・・。

NSStringEncodingの一覧には中国語っぽいものが無いんですよね。
下のように日本語は優遇されている気がしますが、その他言語はどうするんだろうって思いますよね。

enum {
    NSASCIIStringEncoding = 1,		/* 0..127 only */
    NSNEXTSTEPStringEncoding = 2,
    NSJapaneseEUCStringEncoding = 3,
    NSUTF8StringEncoding = 4,
    NSISOLatin1StringEncoding = 5,
    NSSymbolStringEncoding = 6,
    NSNonLossyASCIIStringEncoding = 7,
    NSShiftJISStringEncoding = 8,          /* kCFStringEncodingDOSJapanese */
    NSISOLatin2StringEncoding = 9,
    NSUnicodeStringEncoding = 10,
    NSWindowsCP1251StringEncoding = 11,    /* Cyrillic; same as AdobeStandardCyrillic */
    NSWindowsCP1252StringEncoding = 12,    /* WinLatin1 */
    NSWindowsCP1253StringEncoding = 13,    /* Greek */
    NSWindowsCP1254StringEncoding = 14,    /* Turkish */
    NSWindowsCP1250StringEncoding = 15,    /* WinLatin2 */
    NSISO2022JPStringEncoding = 21,        /* ISO 2022 Japanese encoding for e-mail */
    NSMacOSRomanStringEncoding = 30,

    NSUTF16StringEncoding = NSUnicodeStringEncoding,      /* An alias for NSUnicodeStringEncoding */

    NSUTF16BigEndianStringEncoding = 0x90000100,          /* NSUTF16StringEncoding encoding with explicit endianness specified */
    NSUTF16LittleEndianStringEncoding = 0x94000100,       /* NSUTF16StringEncoding encoding with explicit endianness specified */

    NSUTF32StringEncoding = 0x8c000100,                   
    NSUTF32BigEndianStringEncoding = 0x98000100,          /* NSUTF32StringEncoding encoding with explicit endianness specified */
    NSUTF32LittleEndianStringEncoding = 0x9c000100        /* NSUTF32StringEncoding encoding with explicit endianness specified */
};
typedef NSUInteger NSStringEncoding;

もちろんUTF8ならいいのですが、Windowsで作ったテキストファイルなどはUnicodeになっていないことが多いようです。
日本語でもShiftJISが未だに使われますよね。

日本人でこの問題に言及している方がいなかったので情報共有で書いておきます。

結論を書きますと、CFStringConvertEncodingToNSStringEncodingを使えばその他の文字コードも読み取りできるようです。
中国語(簡体)ではGB 18030というコードが使われるようなので、以下のような感じになります。

NSStringEncoding encode = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
str = [NSString stringWithCString:cstring encoding: encode];

さて、知っている方いらっしゃったら教えてほしいのですが、台湾で使われるトラディッショナル(繁体?)な方のコードと、韓国語はどのようなコードが使われているのでしょうか?
台湾と韓国のユーザーの方もそれなりにいらっしゃるので対応したいのですがそもそもサンプルも無ければハングルを入力する術もないので・・・。

ちなみに、自動判別は無理だと思われます。。

追記:
とりあえずEUC-krが一般的っぽいので、それに対応しておいてみます。
そういうファイルが無いので確認はできませんが・・・。
台湾(?)ではbig5が普通と聞きましたので、それに対応しておいてみます。

ラテン文字はどう区別していいのかわかんないですね。
とりあえず手動で全て選択できるようにしておきます。力技。

カレンダー

2024年11月
 123
45678910
11121314151617
18192021222324
252627282930  

▲Pagetop