エンディアン変換プログラム
ネットワークプログラムを組む際に,Big Endian (Network Byte Order) <--> Little Endian (x86) のエンディアン変換プログラムを書いてみた.(ほとんど参考サイトの写経ともいう.)
/** @file endian_utility.hpp * @brief Header file for utility fanctions to change endian. */ #ifndef ENDIAN_UTILITY_HPP__INCLUDED #define ENDIAN_UTILITY_HPP__INCLUDED #include <boost/cstdint.hpp> //< for boost::uint8_t etc. namespace endian { // check endian for the host CPU inline bool isLittleEndian() { boost::uint16_t test_data = 0x0001; //< 2 bytes data // test data are stored as ... // "00 01" if BigEndian // "01 00" if LittleEndian // So, check the first byte: return (0x01 == (*(reinterpret_cast<boost::uint8_t*>(&test_data)))); } inline bool isBigEndian() { return !isLittleEndian(); } inline boost::uint16_t reverseEndian(boost::uint16_t value) { value = static_cast<boost::uint16_t> ((value & 0x00FF) << 8 | (value & 0xFF00) >> 8); return value; } inline boost::uint32_t reverseEndian(boost::uint32_t value) { value = static_cast<boost::uint32_t> ((value & 0x00FF00FF) << 8 | (value & 0xFF00FF00) >> 8); value = static_cast<boost::uint32_t> ((value & 0x0000FFFF) << 16 | (value & 0xFFFF0000) >> 16); return value; } inline boost::uint64_t reverseEndian(boost::uint64_t value) { value = static_cast<boost::uint64_t> ((value & 0x00FF00FF00FF00FF) << 8 | (value & 0xFF00FF00FF00FF00) >> 8); value = static_cast<boost::uint64_t> ((value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16); value = static_cast<boost::uint64_t> ((value & 0x00000000FFFFFFFF) << 32 | (value & 0xFFFFFFFF00000000) >> 32); return value; } } //< namespace Endian #endif //< ENDIAN_UTILITY_HPP__INCLUDED
参考にしたサイト
追記(2011-05-27)
templateを利用したエンディアン変換プログラムを見つけた:
- http://www.kijineko.co.jp/tech/cpptempls/endian/reverse_endian.html
- http://blog.petermares.com/2010/10/26/snippet-endian-swap-template/
これらの場合,
- Cons: 型に依らない
- Pros: 1 byte ごとの処理になるため,データサイズに線形の処理時間がかかる
と思われる.
singed char と unsigned char の違いに関するメモ
バイナリデータをごりごり直接操作する場合,1byte(8bit)型としてchar型を使用することがある.このとき,符号(signed / unsigned) を意識しないと思わぬ罠にはまるという話の経験談.
結論
- 1byte型は"unsigned" char型を意識して使うほうがよい.
ポイント
- "char"のみと記述した場合,"signed" or "unsigned" のどちらであるのかは,言語仕様で定められていない
- たとえば,VCではコンパイルオプションでどちらとみなすか指定できる.
- char型の「文字型」としての機能を忘れない
- 難しくいえば: バイナリは「具体」・文字は「抽象」
実験
C/C++でchar型の変数は,格納されているバイナリ値に対応するasciiコード表上の文字を表現している.たとえば,0x61というバイナリ値は'a'という文字を表現していると解釈される:
std::cout << boost::format("%X") % (short)'a' << '\n'; // 0x61 std::cout << boost::format("%s") % (unsigned char)97 << '\n'; // a std::cout << boost::format("%s") % (unsigned char)0x61 << '\n'; // a
実行結果:
61 a a
つまり,数値(10進数/バイナリ値)をchar型にキャストすると文字として解釈が可能となる.
さて,数値をchar型にキャストするとき,その数値範囲を気を付ける必要がある.例として,127(0x7F)と128(0x80)を挙げる.
まず,"unsigned char"にキャストしたのちに,数値データに再キャストする:
std::cout << boost::format("%1$d : %1$X") % (short)(unsigned char)127 << '\n'; std::cout << boost::format("%1$d : %1$X") % (short)(unsigned char)128 << '\n';
127 : 7F 128 : 80
これは素直に解釈できて,127-->127, 128-->128と無事に戻ってこれる.
問題は,signed char型に,または,char型を"signed" char型と扱う環境でchar型に,キャストしたのち,数値データに再キャストした場合:
std::cout << boost::format("%1$d : %1$X") % (short)(char)127 << '\n'; std::cout << boost::format("%1$d : %1$X") % (short)(char)128 << '\n';
127 : 7F -128 : FF80
ここでは,127-->127に戻るが,128-->"-128" になってしまう!
たぶん,文字の表現としてasciiコードが7bit(0x7Fまで)であることと,符号付き数値型の負数の表現法が原因.とりあえずメモ.
Hex dump (16進数表示) のプログラム (Boost.Format 使用)
16進数ダンプの関数を書いてみた.(Debugger使うのはおいといて・・・.)
せっかくなので,Boost.Formatを使ってみる.
/** @file hex_dump.hpp * @brief Header file for fanction to display variable in hex. */ #ifndef HEX_DUMP_HPP__INCLUDED #define HEX_DUMP_HPP__INCLUDED #include <iostream> #include <iomanip> #include <boost/cstdint.hpp> #include <boost/format.hpp> template <class T> void dumpHex(const T& t) { for (size_t i = 0; i < sizeof(T); ++i) { std::cout << boost::format("%02X") % (static_cast<unsigned short>(*(reinterpret_cast<boost::uint8_t*>(const_cast<T*>(&t)) + i))) << ' '; } std::cout << '\n'; } // just for your reference template <class T> void dumpHex_noBoost(const T& t) { for (size_t i = 0; i < sizeof(T); ++i) { std::cout << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (static_cast<unsigned short>(*(reinterpret_cast<unsigned char*>(const_cast<T*>(&t)) + i))) << ' '; } std::cout << '\n'; } #endif //< HEX_DUMP_HPP__INCLUDED
テスト:
#include <iostream> #include "hex_dump.hpp" int main() { short shortData = 12345; int intData = 1234567890; long long longlongData = 12345678901234567890; float floatData = 1234.5678f; double doubleData = 9.9e9; std::cout << "size of variable \n"; std::cout << "short: " << sizeof(shortData) << '\n'; std::cout << "int: " << sizeof(intData) << '\n'; std::cout << "long long: " << sizeof(longlongData) << '\n'; std::cout << "float: " << sizeof(floatData) << '\n'; std::cout << "double: " << sizeof(doubleData) << '\n'; std::cout << "\n"; std::cout << "Hex dump \n"; dumpHex(shortData); dumpHex(intData); dumpHex(longlongData); dumpHex(floatData); dumpHex(doubleData); std::cout << "\n"; std::cout << "Hex dump (no Boost ver.) \n"; dumpHex_noBoost(shortData); dumpHex_noBoost(intData); dumpHex_noBoost(longlongData); dumpHex_noBoost(floatData); dumpHex_noBoost(doubleData); std::cout << "\n"; return 0; }
実行結果は次のようになる:
size of variable short: 2 int: 4 long long: 8 float: 4 double: 8 Hex dump 39 30 D2 02 96 49 D2 0A 1F EB 8C A9 54 AB 2B 52 9A 44 00 00 00 18 B0 70 02 42
簡単な解説
上のコードではキャストが乱舞しているが,本質的には
(short)(*((unsigned char*)&t))
である.
&t // 対象データのアドレス
(unsigned char*)&t // 対象データへのポインタ.1 byteデータとして解釈.
*((unsigned char*)&t) // 中身
(short)(*((unsigned char*)&t)) // 中身を数値データとして解釈→表示へ
参考にしたサイト
- とりあえず C++ - ファイルデータを16進数で表示する
- http://www.geocities.jp/eneces_jupiter_jp/cpp1/020-020.html
- iostreamの書式指定を参考にした
- http://www.geocities.jp/eneces_jupiter_jp/cpp1/020-020.html
- How do I display the binary representation of a float or double? - Stack Overflow
- http://stackoverflow.com/questions/397692/how-do-i-display-the-binary-representation-of-a-float-or-double
- std::bitsetを使って2進数表示するサンプルの投稿あり
- http://stackoverflow.com/questions/397692/how-do-i-display-the-binary-representation-of-a-float-or-double
- How can i convert hex numbers to binary in c++? - Stack Overflow
- http://stackoverflow.com/questions/483609/how-can-i-convert-hex-numbers-to-binary-in-c
- char型を数値型表示するminimumなサンプルの投稿あり
- http://stackoverflow.com/questions/483609/how-can-i-convert-hex-numbers-to-binary-in-c
Windows の I/O port 制御のメモ
Windows(NT系)ではユーザーモードからI/O portの制御ができないようだ.(カーネルモードからドライバを使えということらしい.)
ユーザーモードからI/O制御するためのDLL類を色々な人が提供している.定番がよくわからないが,とりあえずメモ:
- WinIo, http://www.internals.com/
- PortTalk, http://www.beyondlogic.org/porttalk/porttalk.htm
- NTPort Library, http://www.zealsoftstudio.com/ntport/
- ほか多数
MicrosoftによるNT系のI/O port制御に関する説明は次:
- DOC: Port I/O with inp() and outp() Fails on Windows NT, http://support.microsoft.com/kb/112298/en-us
Boost.Asio の Timer精度調査 (Windows環境)
要旨
- 目的: Boost.Asio には Timerクラスである deadline_timerがある.この時間精度について調べてみる.
- 調査環境: Boost 1.44.0, Windows XP (x64)
- 結論: date_timeライブラリに依存している.調査環境におけるdate_timeライブラリの時間取得関数はGetSystemTime/GetSystemTimeAsFileTimeである.これらの精度は10ms程度.
boost::asio::deadline_timerクラス
主に関係するヘッダファイルは次の通り:
- boost/asio/deadline_timer.hpp
- basic_deadline_timer に boost::posix_time::ptime を与えてtypedef
- boost/asio/basic_deadline_timer.hpp
- クラス定義.動作はdeadline_timer_serviceに投げる.
- boost/asio/deadline_timer_service.hpp
- 動作の記述.詳細はdetail::deadline_timer_serviceに投げる
- boost/asio/detail/deadline_timer_service.hpp
- 動作の詳細定義.
これより,wait()やasync_***()系の定義は
これらのファイルの中では,時間に関係する部分はtemplateによって記述されている.最終的に,(現在の実装では)
読み進めると,タイマーなど分解能を要する時間に関係する部分は,boost::posix_time::microsec_clock に依存するように書かれている.
date_time の時間精度
主に関係するヘッダファイルは次の通り:
- boost/date_time/microsec_time_clock.hpp
- boost/date_time/filetime_functions.hpp
micro秒の扱いについて定義しているmicrosec_time_clock.hppを見てみると,82行目あたりに次のような定義を見る.
#ifdef BOOST_HAS_GETTIMEOFDAY timeval tv; gettimeofday(&tv, 0); //gettimeofday does not support TZ adjust on Linux. std::time_t t = tv.tv_sec; boost::uint32_t sub_sec = tv.tv_usec; #elif defined(BOOST_HAS_FTIME) winapi::file_time ft; winapi::get_system_time_as_file_time(ft); uint64_t micros = winapi::file_time_to_microseconds(ft); // it will not wrap, since ft is the current time // and cannot be before 1970-Jan-01 std::time_t t = static_cast<std::time_t>(micros / 1000000UL); // seconds since epoch // microseconds -- static casts supress warnings boost::uint32_t sub_sec = static_cast<boost::uint32_t>(micros % 1000000UL); #else #error Internal Boost.DateTime error: BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK is defined, however neither gettimeofday nor FILETIME support is detected. #endif
winapi::get_system_time_as_file_time関数 などは filetime_functions.hpp 内で定義されており,たとえば66行目あたりを見ると次のように定義されている.
typedef FILETIME file_time; typedef SYSTEMTIME system_time; inline void get_system_time_as_file_time(file_time& ft) { #if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3205)) // Some runtime library implementations expect local times as the norm for ctime. file_time ft_utc; GetSystemTimeAsFileTime(&ft_utc); FileTimeToLocalFileTime(&ft_utc, &ft); #elif defined(BOOST_NO_GETSYSTEMTIMEASFILETIME) system_time st; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); #else GetSystemTimeAsFileTime(&ft); #endif }
すなわち,調査環境においては,時間計測はGetSystemTime / GetSystemTimeAsFileTime 関数に依存していることがわかる.これらの関数は精度10ms程度程度である.
結論
調査環境では,Boost.Asioのタイマー系の精度は10ms程度のようであるから,これにあった運用をすべきである.
備考1
Windows環境では,clock (GetSystemTimeAsFileTime依存), GetTickCount, timeGetTime, QueryPerformanceCounter などが利用できる.(TODO: 暇ができたらまとめる・・・か?)
シビアな時間制御をする際は,timeGetTimeや QueryPerformanceCounter を用いて自前で用意をしたほうがよいだろう.
しかし,そもそもAsioの想定しているSocket通信や,非RTOSであるWindowsでそれほどシビアな運用を要求するアプリケーションを作成するのは無茶というもの・・・かもしれない.
備考2
boostのバージョンによって実装は日々変化しているようだ.手元にあった1.44と1.36のソースをざっと見比べてみたが,ファイル構成や関数定義について,さまざまな部分で違いがみられた.
参考
- Reference - Boost 1.46.1, http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/reference.html
SetThreadPriority と SetThreadPriority のメモ
Windowsでプロセスやスレッドの優先度を指定する方法のメモ.
※OSのバージョン等によって,設定値や動作が違う場合がある模様なので注意.
優先度の基本的な考え方
- 0(もっとも低い)〜31(もっとも高い)であらわされる優先度によって,スレッドの実行をスケジューリング
- 優先度 = Process priority class (プロセスの優先度) + Thread priority level (スレッドの優先度)
プロセスの優先度について
プロセスの優先度を任意に変更するためには,たとえば次のように指定する.
// プロセスの優先度設定 → もっとも高い(REALTIME)
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
Priority Class の指定の仕方については,次の順番で優先度が高い.
REALTIME_PRIORITY_CLASS // もっとも優先 HIGH_PRIORITY_CLASS // 優先度高 ABOVE_NORMAL_PRIORITY_CLASS NORMAL_PRIORITY_CLASS // 通常 BELOW_NORMAL_PRIORITY_CLASS IDLE_PRIORITY_CLASS // アイドル時
スレッドの優先度について
スレッドの優先度について変更するためには,たとえば次のように指定する.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL));
プロセスの優先度をもとに,相対値を指定する.次の順番で優先度が高い.
THREAD_PRIORITY_TIME_CRITICAL // 15 (プロセスが REALTIME_PRIORITY_CLASS のとき 31) THREAD_PRIORITY_HIGHEST // 基準 +2 THREAD_PRIORITY_ABOVE_NORMAL // 基準 +1 THREAD_PRIORITY_NORMAL // 基準 THREAD_PRIORITY_BELOW_NORMAL // 基準 -1 THREAD_PRIORITY_LOWEST // 基準 -2 THREAD_PRIORITY_IDLE // 1 (プロセスが REALTIME_PRIORITY_CLASS のとき 16)
Process priority class と Thread priority level の組み合わせ
最終的な優先度の値については,http://msdn.microsoft.com/en-us/library/ms685100(v=VS.85).aspx を参照すれば一目瞭然である.
参考
- SetPriorityClass Function (Windows),http://msdn.microsoft.com/en-us/library/ms686219(VS.85).aspx
- SetThreadPriority Function (Windows),http://msdn.microsoft.com/en-us/library/ms686277(VS.85).aspx
- Scheduling Priorities (Windows),http://msdn.microsoft.com/en-us/library/ms685100(v=VS.85).aspx
SetThreadAffinityMask関数のメモ
http://msdn.microsoft.com/ja-jp/library/cc429346.aspx におけるSetThreadAffinityMask 関数の説明によれば,
DWORD SetThreadAffinityMask ( HANDLE hThread, // 操作対象スレッドのハンドル DWORD dwThreadAffinityMask // スレッドアフィニティマスク );
であり,
スレッドアフィニティマスクはビットベクトルであり、各ビットはそのスレッドを実行できるプロセッサを表します。
とある.
例として,利用可能なcpuコア番号が0,1,2・・・のとき,
SetThreadAffinityMask(GetCurrentThread(), 0x00000001); // 現在のスレッドを0番目のコアに固定 SetThreadAffinityMask(GetCurrentThread(), 0x00000002); // 現在のスレッドを1番目のコアに固定
のようになる.スレッドアフィニティマスクの指定の仕方は,
0x00000001 // (コア0が有効: 0b0001) 0x00000002 // (コア1が有効: 0b0010) 0x00000003 // (コア1,0が有効: 0b0011) 0x00000004 // (コア2が有効: 0b0100) ...
とするようだ.(cpuに対応したビットを立てる)
dwThreadAffinityMask が DWORD型(32bit)なので,この方法だと33コア以上は扱えない様子.
参考
- Windows Data Types (Windows), http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx
- affinity mask Option, http://msdn.microsoft.com/en-us/library/ms187104(SQL.90).aspx