エンディアン変換プログラム

ネットワークプログラムを組む際に,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を利用したエンディアン変換プログラムを見つけた:

これらの場合,

  • 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)) // 中身を数値データとして解釈→表示へ

参考にしたサイト

Windows の I/O port 制御のメモ

Windows(NT系)ではユーザーモードからI/O portの制御ができないようだ.(カーネルモードからドライバを使えということらしい.)

ユーザーモードからI/O制御するためのDLL類を色々な人が提供している.定番がよくわからないが,とりあえずメモ:

MicrosoftによるNT系のI/O port制御に関する説明は次:

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::ptime に依存するようになっている.

読み進めると,タイマーなど分解能を要する時間に関係する部分は,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のソースをざっと見比べてみたが,ファイル構成や関数定義について,さまざまな部分で違いがみられた.

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 を参照すれば一目瞭然である.

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コア以上は扱えない様子.