Entry

本日の勝手にどう書く - IPv6 のアドレスごにょごにょをやってみた

2009年01月27日

こちらの話。うーん……みんな短く書くもんだなぁ,と感心してしまう。

いろんなところでIPv6の実用化やそれに向けた実験が進められており、今後IPv6のアドレスを見かけることも多くなりそうなので少し触れてみよう、というお題です。

IPv6アドレスの短縮 どう書く?org

あたしのコードが長くなるのは,直接文字列を変換していないからなんだと思います。パースしてたらそりゃ長くなるわな,と,若干の言い訳。ともあれ,最近,能書きばっかたれてるので,少しは手を動かした形跡を残しておこう,とか云々。

つことで,こんな感じで書いてみました。cygwin GCC 3.4.4 の g++ でコンパイルと動作確認しています。そいや,cygwin GCC 3.4.4 の g++ って,std::wstring が使えなかったりします。前方宣言も使えなかったような。まともな処理系ないもんか……ここでの話とは関係ないんですけど。

#include <cstdio>
#include <cctype>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

class IPv6Address {
public:
    IPv6Address();
    virtual ~IPv6Address();
private: // no implement
    IPv6Address::IPv6Address(const IPv6Address& other);
    IPv6Address& operator=(const IPv6Address& other);
private: // constants
    static const char DELIMITER;
    static const long SKIPNUM;
    static const int FIELDMAX;
    static const int FIELDMIN;
public:
    void parse(const std::string& adr) throw (std::logic_error);
    std::string getLongAddress(bool padding = true);
    std::string getShortAddress(bool padding = true);
    void reset();
private:
    void normalize(const std::vector<long>& tmp) throw (std::logic_error);
    void addSection(std::string& adr, long sec, bool pad);
private:
    std::vector<long> address_;
};

const char IPv6Address::DELIMITER = ':';
const long IPv6Address::SKIPNUM = -1;
const int IPv6Address::FIELDMAX = 8;
const int IPv6Address::FIELDMIN = 1;

IPv6Address::IPv6Address() {
}

IPv6Address::~IPv6Address() {
}

void
IPv6Address::parse(const std::string& adr) throw (std::logic_error) {
    int delimiter = 0;
    char* ep;  // dummy for strtod(3)
    std::string buf = "";
    std::string::const_iterator itr;
    std::vector<long> temp;

    for (itr = adr.begin(); itr != adr.end(); itr++) {
        if (*itr == DELIMITER) {
            if (delimiter == 0) {
                if (itr != adr.begin()) {
                    temp.push_back(::strtol(buf.c_str(), &ep, 16));
                    buf = "";  // reset buffer
                }
            } else if (delimiter == 1) {
                temp.push_back(SKIPNUM);
                buf = "";  // reset buffer
            } else {
                throw std::logic_error("parse error.");
            }
            delimiter++;
        } else if (::isxdigit(*itr)) {
            buf += *itr;
            delimiter = 0;
        } else {
            throw std::logic_error("parse error.");
        }
    }
    // push the rest of the field.
    temp.push_back(::strtol(buf.c_str(), &ep, 16));
    // normalize
    normalize(temp);

    return;
}

void
IPv6Address::normalize(const std::vector<long>& tmp) throw (std::logic_error) {
    int skips = 0;
    int fields = tmp.size();
    std::vector<long>::const_iterator vitr;
    // check
    for (vitr = tmp.begin(); vitr != tmp.end(); vitr++) {
        if (*vitr == SKIPNUM) {
            skips++;
        }
    }
    if (skips > 1 || fields < FIELDMIN || fields > FIELDMAX) {
        throw std::logic_error("parse error.");
    }
    // copy and pad
    int padding = FIELDMAX - fields;
    for (vitr = tmp.begin(); vitr != tmp.end(); vitr++) {
        if (*vitr == SKIPNUM) {
            for (int i = 0; i <= padding; i++) {
                address_.push_back(0);
            }
        } else if (*vitr > 0xffff) {
            throw std::logic_error("parse error.");
        } else {
            address_.push_back(*vitr);
        }
    }
    return;
}

void
IPv6Address::addSection(std::string& adr, long sec, bool padding) {
    char buf[5] = {0};  // max value is 0xffff
    if (padding) {
        ::sprintf(buf, "%04x", sec);
    } else {
        ::sprintf(buf, "%x", sec);
    }
    adr += buf;
}

std::string
IPv6Address::getLongAddress(bool padding) {
    std::string ret = "";
    char buf[5] = {0};  // max value is 0xffff
    if (address_.size() > 0) {
        std::vector<long>::const_iterator itr;
        for (itr = address_.begin(); itr != address_.end(); itr++) {
            if (itr != address_.begin()) {
                ret += ':';
            }
            addSection(ret, *itr, padding);
        }
    }
    return ret;
}

std::string
IPv6Address::getShortAddress(bool padding) {
    std::string ret = "";
    if (address_.size() > 0) {
        std::vector<long>::const_iterator itr;
        bool shortened = false;
        for (itr = address_.begin(); itr != address_.end(); itr++) {
            if (*itr == 0 && !shortened) {
                ret += ":";
                for ( ; itr != address_.end() && *itr == 0; itr++) {
                    shortened = true;
                }
                if (itr == address_.end()) {
                    ret += ":";
                }
                itr--;  // push back
            } else {
                if (itr != address_.begin()) {
                    ret += ":";
                }
                addSection(ret, *itr, padding);
            }
        }
    }

    return ret;
}

void
IPv6Address::reset() {
    address_.clear();
}

////////////////////////////////////////////////////////////////////////////////
void
test(const std::string& adr) {
    try {
        IPv6Address ipa;
        ipa.parse(adr);
        std::cout << "INPUT " << adr << std::endl;
        std::cout << "L-PAD " << ipa.getLongAddress() << std::endl;
        std::cout << "L-NOP " << ipa.getLongAddress(false) << std::endl;
        std::cout << "S-PAD " << ipa.getShortAddress() << std::endl;
        std::cout << "S-NOP " << ipa.getShortAddress(false) << std::endl;
        std::cout << std::endl;
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    } catch (...) {
        std::cerr << "unknown error" << std::endl;
    }
}

int
main(int argc, char* argv[]) {
    test("1234:5678:0000:0000:1234:0000:0000:9abc");
    test("f0f0:0100:0020:0003:1000:0100:0020:0003");
    test("1230:5670:0000:0000:0123:0000:0000:00ab");
    test("0000:0000:0000:0000:0000:0000:0000:0001");
    test("0001:0000:0000:0000:0000:0000:0000:0000");
    test("0000:0000:0000:0000:0000:0000:0000:0000");
    test("0001:0001:0001:0002:0002:0002:0003:0003");
    test("1234:5678:0000:0000:1234::9abc");
    test("123:567::123:0000:0000:00ab");
    test("::1");
    test("1::");
    test("::");
    return 0;
}

一旦 long 値の vector で正規化しておいて,表示部分で適宜整形しています。こゆ風に,表示形式と内部で持つデータ形式を分けるっつのは,あたしの癖みたいなもんで,そのせいでコードが長くなることはよくあります。ただ,正規化したデータ構造に読み込むパース部を一旦作っておくと(読み込み部と書き出し部を分離しておくと),出力部の変更に読み込み部が影響を受けない,という特典があります。んなもんで,ちとお題からは外れてるんですけど,省略記法を展開する方のメソッドも入れてみました。

また,圧縮するメソッドは,最初に見つけた 0 の並びを無条件に縮めてるんですけど,本当だったら,一番長い 0 の並びを圧縮するのが親切なんだと思います。

ま,なんつかですね,手慰みっつかですでね,現実逃避っつかですね,そゆもんです。現実逃避で作ったもんって,案外傑作が多かったりするんだけれど,今回のコレはどうだろう。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN