diff --git a/.gitignore b/.gitignore
index 094c4537..666b134c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
.dirstamp
/Doxyfile
/install-sh
+/ip.cpp
/json.cpp
/libgim-*.tar.*
.libs
diff --git a/Makefile.am b/Makefile.am
index 32049414..317ce6b5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,7 @@ UTIL_INCLUDE = \
except.hpp \
float.hpp \
io.hpp \
+ ip.hpp \
json.hpp \
maths.hpp \
matrix.hpp \
@@ -30,6 +31,7 @@ UTIL_FILES = \
except.cpp \
float.cpp \
io.cpp \
+ ip.cpp \
json.cpp \
maths.cpp \
matrix.cpp \
@@ -40,8 +42,8 @@ UTIL_FILES = \
vector.cpp \
version.cpp
-CLEANFILES = json.cpp version.cpp
-EXTRA_DIST = json.cpp.rl version.cpp.rl
+CLEANFILES = json.cpp version.cpp ip.cpp
+EXTRA_DIST = json.cpp.rl version.cpp.rl ip.cpp.rl
RAGELFLAGS = -F1
SUFFIXES = .cpp .cpp.rl
diff --git a/ip.cpp.rl b/ip.cpp.rl
new file mode 100644
index 00000000..b1a8995b
--- /dev/null
+++ b/ip.cpp.rl
@@ -0,0 +1,107 @@
+/*
+ * This file is part of libgim.
+ *
+ * libgim is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * libgim is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libgim. If not, see .
+ *
+ * Copyright 2011 Danny Robson
+ */
+
+
+#include "ip.hpp"
+
+#include
+#include
+
+
+using namespace std;
+
+
+ipv4::ip::ip (uint8_t a, uint8_t b, uint8_t c, uint8_t d):
+ m_octets ({ a, b, c, d })
+{ ; }
+
+
+ipv4::ip&
+ipv4::ip::operator = (const ipv4::ip &rhs) {
+ m_octets[0] = rhs.m_octets[0];
+ m_octets[1] = rhs.m_octets[1];
+ m_octets[2] = rhs.m_octets[2];
+ m_octets[3] = rhs.m_octets[3];
+
+ return *this;
+}
+
+
+bool
+ipv4::ip::operator == (const ipv4::ip &rhs) const {
+ return m_octets[0] == rhs.m_octets[0] &&
+ m_octets[1] == rhs.m_octets[1] &&
+ m_octets[2] == rhs.m_octets[2] &&
+ m_octets[3] == rhs.m_octets[3];
+}
+
+
+// RFC 3986
+%%{
+ machine ipv4;
+ octet = ( [0-9][0-9]? |
+ '1'[0-9][0-9] |
+ '2'[0-4][0-9] |
+ '25'[0-5])
+ > {
+ octetstart = fpc;
+ }
+ % {
+ octetend = fpc;
+ __octet = 0;
+
+ for (auto i = octetstart; i < octetend; ++i)
+ __octet = __octet * 10 + *i - '0';
+ };
+
+ ipv4 := (octet %{ __octets[0] = __octet; } '.'
+ octet %{ __octets[1] = __octet; } '.'
+ octet %{ __octets[2] = __octet; } '.'
+ octet %{ __octets[3] = __octet; })
+ > { __success = false; }
+ % { __success = true; }
+ $!{ __success = false; };
+}%%
+
+%%write data;
+
+
+ipv4::ip
+ipv4::ip::parse (const string &data) {
+ bool __success = true;
+ uint8_t __octets[4];
+ const char *octetstart, *octetend;
+ uint8_t __octet;
+
+ int cs = 0;
+ const char *p = data.data (),
+ *pe = p + data.size (),
+ *eof = pe;
+
+ %%write init;
+ %%write exec;
+
+ if (!__success)
+ throw invalid_argument(data);
+
+ return ipv4::ip(__octets[0],
+ __octets[1],
+ __octets[2],
+ __octets[3]);
+}
diff --git a/ip.hpp b/ip.hpp
new file mode 100644
index 00000000..4829a5eb
--- /dev/null
+++ b/ip.hpp
@@ -0,0 +1,44 @@
+/*
+ * This file is part of libgim.
+ *
+ * libgim is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * libgim is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libgim. If not, see .
+ *
+ * Copyright 2011 Danny Robson
+ */
+
+#ifndef __UTIL_IP_HPP
+#define __UTIL_IP_HPP
+
+#include
+#include
+
+
+namespace ipv4 {
+ struct ip {
+ uint8_t m_octets[4];
+
+ ip (uint8_t a, uint8_t b, uint8_t c, uint8_t d);
+
+ ip& operator = (const ip &);
+ bool operator == (const ip &) const;
+
+ static ip parse (const std::string &);
+ };
+
+
+ typedef uint16_t port_t;
+}
+
+
+#endif // __UTIL_IP_HPP
diff --git a/test/.gitignore b/test/.gitignore
index 5bb89202..5a6e99aa 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,7 +1,8 @@
/backtrace
/float
-/range
+/ip
+/json-check
/maths
/matrix
+/range
/version
-/json-check
diff --git a/test/Makefile.am b/test/Makefile.am
index 02ffb6e8..f641d486 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,7 +7,7 @@ AM_CPPFLAGS = \
AM_LDFLAGS = $(COMMON_LDFLAGS)
-TEST_BIN = backtrace float range maths matrix version
+TEST_BIN = backtrace float range maths matrix version ip
TESTS = $(TEST_BIN) json.pl
check_PROGRAMS = $(TEST_BIN) json-check
EXTRA_DIST = json.pl
@@ -31,5 +31,8 @@ matrix_LDADD = $(builddir)/../libutil.la $(BOOST_SYSTEM_LIB)
version_SOURCES = version.cpp
version_LDADD = $(builddir)/../libutil.la $(BOOST_SYSTEM_LIB)
+ip_SOURCES = ip.cpp
+ip_LDADD = $(builddir)/../libutil.la $(BOOST_SYSTEM_LIB)
+
json_check_SOURCES = json-check.cpp
json_check_LDADD = $(builddir)/../libutil.la $(BOOST_FILESYSTEM_LIB)
diff --git a/test/ip.cpp b/test/ip.cpp
new file mode 100644
index 00000000..78eab909
--- /dev/null
+++ b/test/ip.cpp
@@ -0,0 +1,28 @@
+
+#include "../ip.hpp"
+
+#include "../debug.hpp"
+#include "../types.hpp"
+
+#include
+
+
+using namespace std;
+
+
+int
+main (int argc, char **argv) {
+ struct ip_test {
+ const char *str;
+ const ipv4::ip ip;
+ } data [] = {
+ { "0.0.0.0", { 0, 0, 0, 0 } },
+ { "255.255.255.255", { 255, 255, 255, 255 } },
+ { "127.0.0.1", { 127, 0, 0, 1 } }
+ };
+
+ for (unsigned int i = 0; i < elems (data); ++i)
+ check_hard (ipv4::ip::parse (data[i].str) == data[i].ip);
+
+ return EXIT_SUCCESS;
+}