Thread: [Assorted-commits] SF.net SVN: assorted:[1250] cpp-commons/trunk/src
Brought to you by:
yangzhang
From: <yan...@us...> - 2009-03-04 23:31:12
|
Revision: 1250 http://assorted.svn.sourceforge.net/assorted/?rev=1250&view=rev Author: yangzhang Date: 2009-03-04 23:31:02 +0000 (Wed, 04 Mar 2009) Log Message: ----------- - refactored st_reader out of st.h into streamreader.h as stream_reader - added stream_writer from ydb (writer) - added unit tests for stream_reader, stream_writer - cleaned up stream_reader a bit - Modified Paths: -------------- cpp-commons/trunk/src/commons/exceptions.h cpp-commons/trunk/src/commons/st/st.h Added Paths: ----------- cpp-commons/trunk/src/commons/streamreader.h cpp-commons/trunk/src/commons/streamwriter.h cpp-commons/trunk/src/test/streamreader.cc cpp-commons/trunk/src/test/streamwriter.cc Modified: cpp-commons/trunk/src/commons/exceptions.h =================================================================== --- cpp-commons/trunk/src/commons/exceptions.h 2009-03-04 23:30:45 UTC (rev 1249) +++ cpp-commons/trunk/src/commons/exceptions.h 2009-03-04 23:31:02 UTC (rev 1250) @@ -19,6 +19,16 @@ const string msg; }; + class msg_exception : public std::exception + { + public: + msg_exception(const string &msg) : msg_(msg) {} + ~msg_exception() throw() {} + const char *what() const throw() { return msg_.c_str(); } + private: + const string msg_; + }; + #define throw_operation_not_supported() throw operation_not_supported(__PRETTY_FUNCTION__) } Modified: cpp-commons/trunk/src/commons/st/st.h =================================================================== --- cpp-commons/trunk/src/commons/st/st.h 2009-03-04 23:30:45 UTC (rev 1249) +++ cpp-commons/trunk/src/commons/st/st.h 2009-03-04 23:31:02 UTC (rev 1250) @@ -8,6 +8,7 @@ #include <commons/array.h> #include <commons/delegates.h> #include <commons/nullptr.h> +#include <commons/streamreader.h> #include <commons/sockets.h> #include <commons/utility.h> #include <exception> @@ -372,172 +373,49 @@ std::set<st_thread_t> ts; }; - class eof_exception : public std::exception { - const char *what() const throw() { return "EOF"; } + class st_read_fn + { + private: + st_netfd_t fd_; + st_utime_t to_; + public: + st_read_fn(st_netfd_t fd, st_utime_t to = ST_UTIME_NO_TIMEOUT) + : fd_(fd), to_(to) {} + size_t operator()(char *buf, size_t len) { + return size_t(checknnegerr(st_read(fd_, buf, len, to_))); + } }; - /** - * Convenience class for reading from sockets. - */ - class st_reader + class st_read_fully_fn { - NONCOPYABLE(st_reader) - public: - st_reader(st_netfd_t fd, char *buf, size_t bufsize) : - fd_(fd), - buf_(buf, bufsize), - start_(buf_.get()), - end_(buf_.get()) - {} + private: + st_netfd_t fd_; + st_utime_t to_; + public: + st_read_fully_fn(st_netfd_t fd, st_utime_t to = ST_UTIME_NO_TIMEOUT) + : fd_(fd), to_(to) {} + void operator()(char *buf, size_t len) { + checkeqnneg(st_read_fully(fd_, buf, len, to_), ssize_t(len)); + } + }; - /** - * The size of the unconsumed range of bytes. - */ - size_t unread() { return end_ - start_; } - - /** - * The remaining number of bytes in the buffer - */ - size_t rem() { return buf_.end() - end_; } - - /** - * The entire read buffer. - */ - sized_array<char> &buf() { return buf_; } - - /** - * Manually update/adjust the pointers; useful after changing buf_. - */ - void reset_range(char *start, char *end) { - start_ = start; - end_ = end; - } - - /** - * Discard the requested number of bytes. - */ - void skip(size_t req, st_utime_t to = ST_UTIME_NO_TIMEOUT) { - while (true) { - if (unread() >= req) { - // We have more unconsumed bytes than requested, so we're done. - start_ += req; - break; - } - - // We have more requested bytes than unconsumed, so need to keep - // reading. Skip over bytes... - req -= unread(); - // ...and reset pointers to discard current buffer. - start_ = end_ = buf_.get(); - - ssize_t res = checknnegerr(st_read(fd_, end_, rem(), to)); - end_ += res; - - // If we got a premature EOF. - if (res == 0 && unread() < req) throw eof_exception(); - } - } - - /** - * Returns a char array that contains the requested number of bytes. If - * we hit an error or EOF, then an exception is thrown. - */ - managed_array<char> read(size_t req, st_utime_t to = ST_UTIME_NO_TIMEOUT) { - // Do we already have the requested data? - if (unread() >= req) { - managed_array<char> p(start_, false); - start_ += req; - return p; - } - - // Handle large arrays specially. - if (req > buf_.size()) { - managed_array<char> p(new char[req], true); - memcpy(p.get(), start_, unread()); - checkeqnneg(st_read_fully(fd_, p + unread(), req - unread(), to), static_cast<ssize_t>(req - unread())); - start_ = end_ = buf_.get(); - return p; - } - - // Shift things down if necessary. - if (req > static_cast<size_t>(buf_.end() - end_)) { - memmove(buf_.get(), start_, unread()); - size_t diff = start_ - buf_.get(); - start_ -= diff; - end_ -= diff; - } - - // Keep reading until we have enough. - while (unread() < req) { - ssize_t res = checknnegerr(st_read(fd_, end_, rem(), to)); - if (res == 0) break; - else end_ += res; - } - - // If we got a premature EOF. - if (unread() < req) - throw eof_exception(); - - managed_array<char> p(start_, false); - start_ += req; - return p; - } - - template<typename T> - T read(st_utime_t to = ST_UTIME_NO_TIMEOUT) - { - size_t req = sizeof(T); - - // Do we already have the requested data? - if (unread() >= req) { - T x = *reinterpret_cast<const T*>(start_); - start_ += req; - return x; - } - - assert(req <= buf_.size()); - - // Shift things down if necessary. - if (req > static_cast<size_t>(buf_.end() - end_)) { - memmove(buf_.get(), start_, unread()); - size_t diff = start_ - buf_.get(); - start_ -= diff; - end_ -= diff; - } - - // Keep reading until we have enough. - while (unread() < req) { - ssize_t res = checknnegerr(st_read(fd_, end_, rem(), to)); - if (res == 0) break; - else end_ += res; - } - - // If we got a premature EOF. - if (unread() < req) - throw eof_exception(); - - T x = *reinterpret_cast<const T*>(start_); - start_ += req; - return x; - } - - private: - st_netfd_t fd_; - - /** - * The temporary storage buffer. - */ - sized_array<char> buf_; - - /** - * The start of the unconsumed range of bytes. - */ - char *start_; - - /** - * The end of the unconsumed range of bytes. - */ - char *end_; + class st_reader + { + EXPAND(stream_reader) + private: + stream_reader r_; + public: + st_reader(st_netfd_t fd, char *buf, size_t len) : + r_(st_read_fn(fd), st_read_fully_fn(fd), buf, len) {} + sized_array<char> &buf() { return r_.buf(); } + void reset_range(char *start, char *end) { r_.reset_range(start, end); } + char *start() { return r_.start(); } + char *end() { return r_.end(); } + bool accum(size_t req) { return r_.accum(req); } + void skip(size_t req) { r_.skip(req); } + managed_array<char> read(size_t req) { return r_.read(req); } + template<typename T> T read() { return r_.read<T>(); } + void shift() { r_.shift(); } }; } Added: cpp-commons/trunk/src/commons/streamreader.h =================================================================== --- cpp-commons/trunk/src/commons/streamreader.h (rev 0) +++ cpp-commons/trunk/src/commons/streamreader.h 2009-03-04 23:31:02 UTC (rev 1250) @@ -0,0 +1,247 @@ +#ifndef COMMONS_STREAMREADER_H +#define COMMONS_STREAMREADER_H + +#include <boost/function.hpp> +#include <commons/array.h> +#include <cstring> + +namespace commons { + + using namespace boost; + using namespace commons; + using namespace std; + + class eof_exception : public std::exception { + const char *what() const throw() { return "EOF"; } + }; + + /** + * Convenience wrapper for full-reading from data source streams. + */ + class repeat_reader + { + private: + boost::function<size_t(char*, size_t)> &reader_; + public: + repeat_reader(boost::function<size_t(char*, size_t)> &reader) + : reader_(reader) {} + void operator()(char *buf, size_t len) { + size_t res = 0; + while (res < len) { + res += reader_(buf + res, len - res); + if (res == 0) throw eof_exception(); + } + assert(res == len); + } + }; + + /** + * General-purpose stream reader for arbitrary data source streams. + */ + class stream_reader + { + NONCOPYABLE(stream_reader) + public: + stream_reader(boost::function<size_t(char*, size_t)> reader, + char *buf, size_t bufsize) : + reader_(reader), + full_reader_(repeat_reader(reader_)), + buf_(buf, bufsize), + start_(buf), + end_(buf) + {} + + stream_reader(boost::function<size_t(char*, size_t)> reader, + boost::function<void(char*, size_t)> full_reader, + char *buf, size_t bufsize) : + reader_(reader), + full_reader_(full_reader), + buf_(buf, bufsize), + start_(buf), + end_(buf) + {} + + /** + * The size of the unconsumed range of bytes. + */ + size_t unread() { return end_ - start_; } + + /** + * The remaining number of bytes in the buffer + */ + size_t rem() { return buf_.end() - end_; } + + /** + * The entire read buffer. + */ + sized_array<char> &buf() { return buf_; } + + /** + * Manually update/adjust the pointers; useful after changing buf_. + */ + void reset_range(char *start, char *end) { + start_ = start; + end_ = end; + } + + char *start() { return start_; } + char *end() { return end_; } + + /** + * Accumulate (but don't read/copy) the requested number of bytes. + */ + bool accum(size_t req) { + while (unread() < req && rem() > 0) { + size_t res = reader_(end_, rem()); + if (res == 0) throw eof_exception(); + end_ += res; + } + if (unread() < req) { + start_ = end_; + return false; + } else { + start_ += req; + return true; + } + } + + /** + * Discard the requested number of bytes. + */ + void skip(size_t req) { + if (unread() >= req) { + // We have more unconsumed bytes than requested, so we're done. + start_ += req; + return; + } + + // We have more requested bytes than unconsumed, so need to keep + // reading. Skip over bytes that are immediately available... + req -= unread(); + // ...and reset pointers to discard current buffer. + start_ = end_ = buf_.get(); + + // Keep reading until we have enough. + while (true) { + size_t res = reader_(end_, rem()); + if (res == 0) throw eof_exception(); + if (res == req) break; + if (res > req) { + // Now skip over the rest of the bytes, which weren't available. + start_ += req; + end_ += res; + break; + } + req -= res; + } + } + + /** + * Returns a char array that contains the requested number of bytes. If + * we hit an error or EOF, then an exception is thrown. + */ + managed_array<char> read(size_t req) { + // Do we already have the requested data? + if (unread() >= req) { + managed_array<char> p(start_, false); + start_ += req; + return p; + } + + // Handle large arrays specially. + if (req > buf_.size()) { + managed_array<char> p(new char[req], true); + memcpy(p.get(), start_, unread()); + full_reader_(p + unread(), req - unread()); + start_ = end_ = buf_.get(); + return p; + } + + // Shift things down if necessary. + if (req > static_cast<size_t>(buf_.end() - end_)) + shift(); + + // Keep reading until we have enough. + while (unread() < req) { + size_t res = reader_(end_, rem()); + if (res == 0) break; + else end_ += res; + } + + // If we got a premature EOF. + if (unread() < req) + throw eof_exception(); + + managed_array<char> p(start_, false); + start_ += req; + return p; + } + + template<typename T> + T read() + { + size_t req = sizeof(T); + + // Do we already have the requested data? + if (unread() >= req) { + T x = *reinterpret_cast<const T*>(start_); + start_ += req; + return x; + } + + assert(req <= buf_.size()); + + // Shift things down if necessary. + if (req > static_cast<size_t>(buf_.end() - end_)) + shift(); + + // Keep reading until we have enough. + while (unread() < req) { + size_t res = reader_(end_, rem()); + if (res == 0) break; + else end_ += res; + } + + // If we got a premature EOF. + if (unread() < req) + throw eof_exception(); + + T x = *reinterpret_cast<const T*>(start_); + start_ += req; + return x; + } + + /** + * Shift the unread bytes down to the start of the buffer. + */ + void shift() { + memmove(buf_.get(), start_, unread()); + size_t diff = start_ - buf_.get(); + start_ -= diff; + end_ -= diff; + } + + private: + boost::function<size_t(char*, size_t)> reader_; + + boost::function<void(char*, size_t)> full_reader_; + + /** + * The temporary storage buffer. + */ + sized_array<char> buf_; + + /** + * The start of the unconsumed range of bytes. + */ + char *start_; + + /** + * The end of the unconsumed range of bytes. + */ + char *end_; + }; + +} + +#endif Added: cpp-commons/trunk/src/commons/streamwriter.h =================================================================== --- cpp-commons/trunk/src/commons/streamwriter.h (rev 0) +++ cpp-commons/trunk/src/commons/streamwriter.h 2009-03-04 23:31:02 UTC (rev 1250) @@ -0,0 +1,92 @@ +#ifndef COMMONS_STREAMWRITER_H +#define COMMONS_STREAMWRITER_H + +#include <boost/function.hpp> +#include <commons/array.h> +#include <cstring> +#include <iostream> +#include <iomanip> + +namespace commons { + +using namespace boost; +using namespace commons; +using namespace std; + +class stream_writer +{ + NONCOPYABLE(stream_writer) +private: + sized_array<char> a_; + char *unsent_; + char *mark_; + char *p_; + boost::function<void(void*, size_t)> flushcb; + char *reserve(int n, char *p) { + if (p + n > a_.end()) { + // check that the reserved space will fit + assert(size_t(p - mark_ + n + sizeof(uint32_t)) <= a_.size()); + // get rid of what we have + flush(); + size_t diff = mark_ - (a_.get() + sizeof(uint32_t)); + memmove(a_.get() + sizeof(uint32_t), mark_, p_ - mark_); + mark_ = (unsent_ = a_.get()) + sizeof(uint32_t); + p_ -= diff; + p -= diff; + } + return p; + } + char *prefix() { return mark_ - sizeof(uint32_t); } + template<typename T> + void write_(T x, char *p) { + *reinterpret_cast<T*>(reserve(sizeof x, p)) = x; + } +public: + stream_writer(boost::function<void(void*, size_t)> flushcb, char *a, size_t buf_size) : + a_(a, buf_size), unsent_(a_.get()), mark_(unsent_ + sizeof(uint32_t)), + p_(mark_), flushcb(flushcb) {} + sized_array<char> &buf() { return a_; } + char *cur() { return p_; } + size_t pos() { return p_ - mark_; } + size_t size() { return a_.size(); } + void mark() { + if (p_ > mark_) { + // prefix last segment with its length + *reinterpret_cast<uint32_t*>(prefix()) = uint32_t(p_ - mark_); + // start new segment + mark_ = (p_ += sizeof(uint32_t)); + } + } + void reset() { p_ = mark_; } + void reserve(int n) { reserve(n, p_); } + void mark_and_flush() { + mark(); + flush(); + mark_ = p_ = (unsent_ = a_.get()) + sizeof(uint32_t); + } + void flush() { + if (prefix() > unsent_) { + flushcb(unsent_, prefix() - unsent_); + unsent_ = prefix(); + } + } + template<typename T> void skip() { reserve(sizeof(T)); p_ += sizeof(T); } + template<typename T> void write(T x) { write_(x, p_); p_ += sizeof x; } + template<typename T> void write(T x, size_t off) { write_(x, mark_ + off); } + void show() { + cout << static_cast<void*>(p_); + for (size_t i = 0; i < a_.size(); ++i) + cout << " " << hex << setfill('0') << setw(2) + << int(static_cast<unsigned char>(a_.get()[i])); + cout << endl; + cout << static_cast<void*>(p_); + for (size_t i = 0; i < a_.size(); ++i) + cout << " " << setfill(' ') << setw(2) << (i == pos() ? "^^" : ""); + cout << endl; + } +}; + + +} + +#endif Added: cpp-commons/trunk/src/test/streamreader.cc =================================================================== --- cpp-commons/trunk/src/test/streamreader.cc (rev 0) +++ cpp-commons/trunk/src/test/streamreader.cc 2009-03-04 23:31:02 UTC (rev 1250) @@ -0,0 +1,69 @@ +#include <commons/array.h> +#include <commons/streamreader.h> +#include <gtest/gtest.h> +using namespace commons; +using namespace testing; + +struct rfn { + size_t chunklen_; + array<char> &src_; + char *cur_; + rfn(size_t chunklen, array<char> &src) + : chunklen_(chunklen), src_(src), cur_(src.get()) {} + size_t operator()(char *buf, size_t len) { + len = min(chunklen_, len); + memcpy(buf, cur_, len); + cur_ += len; + return len; + } +}; + +TEST(reader, read) { + array<char> src(1024); + for (int i = 0; i < 10; ++i) + *(reinterpret_cast<int*>(src.get()) + i) = i; + + for (size_t chunklen = 1; chunklen < 50; ++chunklen) { + { + array<char> rbuf(9); + stream_reader r(rfn(chunklen, src), rbuf.get(), rbuf.size()); + for (int i = 0; i < 10; ++i) + EXPECT_EQ(i, r.read<int>()); + } + { + array<char> rbuf(9); + stream_reader r(rfn(chunklen, src), rbuf.get(), rbuf.size()); + managed_array<char> ma = r.read(5 * sizeof(int)); + array<char> src2(1024); + memcpy(src2.get(), ma.get(), 5 * sizeof(int)); + array<char> rbuf2(1024); + stream_reader r2(rfn(chunklen, src2), rbuf2.get(), rbuf2.size()); + for (int i = 0; i < 5; ++i) + EXPECT_EQ(i, r2.read<int>()); + for (int i = 5; i < 10; ++i) + EXPECT_EQ(i, r.read<int>()); + } + { + array<char> rbuf(9); + stream_reader r(rfn(chunklen, src), rbuf.get(), rbuf.size()); + r.skip(9 * sizeof(int)); + EXPECT_EQ(9, r.read<int>()); + } + { + array<char> rbuf(9); + stream_reader r(rfn(chunklen, src), rbuf.get(), rbuf.size()); + EXPECT_TRUE(r.accum(sizeof(int))); + EXPECT_TRUE(r.accum(sizeof(int))); + EXPECT_FALSE(r.accum(sizeof(int))); + r.shift(); + EXPECT_TRUE(r.accum(sizeof(int))); + EXPECT_TRUE(r.accum(sizeof(int))); + EXPECT_FALSE(r.accum(sizeof(int))); + } + } +} + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} Added: cpp-commons/trunk/src/test/streamwriter.cc =================================================================== --- cpp-commons/trunk/src/test/streamwriter.cc (rev 0) +++ cpp-commons/trunk/src/test/streamwriter.cc 2009-03-04 23:31:02 UTC (rev 1250) @@ -0,0 +1,34 @@ +#include <commons/array.h> +#include <commons/streamwriter.h> +#include <gtest/gtest.h> +using namespace commons; +using namespace testing; + +struct wfn { + array<char> &dst_; + char *cur_; + wfn(array<char> &dst) + : dst_(dst), cur_(dst.get()) {} + void operator()(void *buf, size_t len) { + memcpy(cur_, buf, len); + cur_ += len; + } +}; + +TEST(writer, write) { + array<char> dst(1024); + for (size_t wbufsize = 100; wbufsize < 101; ++wbufsize) { + array<char> wbuf(wbufsize); + stream_writer w(wfn(dst), wbuf.get(), wbuf.size()); + for (int i = 0; i < 10; ++i) + w.write(i); + w.mark_and_flush(); + for (int i = 0; i < 10; ++i) + EXPECT_EQ(i, *(reinterpret_cast<int*>(dst.get()) + i + 1)); + } +} + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <yan...@us...> - 2009-03-05 23:14:58
|
Revision: 1252 http://assorted.svn.sourceforge.net/assorted/?rev=1252&view=rev Author: yangzhang Date: 2009-03-05 23:14:45 +0000 (Thu, 05 Mar 2009) Log Message: ----------- - accum returns residual count instead of bool - added anchor, reset Modified Paths: -------------- cpp-commons/trunk/src/commons/streamreader.h cpp-commons/trunk/src/test/streamreader.cc Modified: cpp-commons/trunk/src/commons/streamreader.h =================================================================== --- cpp-commons/trunk/src/commons/streamreader.h 2009-03-04 23:31:58 UTC (rev 1251) +++ cpp-commons/trunk/src/commons/streamreader.h 2009-03-05 23:14:45 UTC (rev 1252) @@ -48,7 +48,8 @@ full_reader_(repeat_reader(reader_)), buf_(buf, bufsize), start_(buf), - end_(buf) + end_(buf), + anchor_(buf) {} stream_reader(boost::function<size_t(char*, size_t)> reader, @@ -58,7 +59,8 @@ full_reader_(full_reader), buf_(buf, bufsize), start_(buf), - end_(buf) + end_(buf), + anchor_(buf) {} /** @@ -80,33 +82,47 @@ * Manually update/adjust the pointers; useful after changing buf_. */ void reset_range(char *start, char *end) { - start_ = start; + anchor_ = start_ = start; end_ = end; } + void set_anchor() { anchor_ = start_; } + + /** + * Reset the pointers, emptying the buffer. + */ + void reset() { + start_ = end_ = anchor_ = buf_.get(); + } + char *start() { return start_; } char *end() { return end_; } + char *anchor() { return anchor_; } /** - * Accumulate (but don't read/copy) the requested number of bytes. + * Accumulate (treat as read, but don't discard, and don't actually + * copy for reading) the requested number of bytes. Returns the + * remaining number of requested bytes that couldn't be accumulated + * because we ran out of buffer space. */ - bool accum(size_t req) { + size_t accum(size_t req) { while (unread() < req && rem() > 0) { size_t res = reader_(end_, rem()); if (res == 0) throw eof_exception(); end_ += res; } if (unread() < req) { + size_t ret = req - unread(); start_ = end_; - return false; + return ret; } else { start_ += req; - return true; + return 0; } } /** - * Discard the requested number of bytes. + * Discard the requested number of bytes. Disregards anchor. */ void skip(size_t req) { if (unread() >= req) { @@ -119,7 +135,7 @@ // reading. Skip over bytes that are immediately available... req -= unread(); // ...and reset pointers to discard current buffer. - start_ = end_ = buf_.get(); + reset(); // Keep reading until we have enough. while (true) { @@ -153,7 +169,7 @@ managed_array<char> p(new char[req], true); memcpy(p.get(), start_, unread()); full_reader_(p + unread(), req - unread()); - start_ = end_ = buf_.get(); + reset(); return p; } @@ -240,6 +256,11 @@ * The end of the unconsumed range of bytes. */ char *end_; + + /** + * Do not discard bytes >= this. + */ + char *anchor_; }; } Modified: cpp-commons/trunk/src/test/streamreader.cc =================================================================== --- cpp-commons/trunk/src/test/streamreader.cc 2009-03-04 23:31:58 UTC (rev 1251) +++ cpp-commons/trunk/src/test/streamreader.cc 2009-03-05 23:14:45 UTC (rev 1252) @@ -52,13 +52,13 @@ { array<char> rbuf(9); stream_reader r(rfn(chunklen, src), rbuf.get(), rbuf.size()); - EXPECT_TRUE(r.accum(sizeof(int))); - EXPECT_TRUE(r.accum(sizeof(int))); - EXPECT_FALSE(r.accum(sizeof(int))); + EXPECT_EQ(0, r.accum(sizeof(int))); + EXPECT_EQ(0, r.accum(sizeof(int))); + EXPECT_EQ(3, r.accum(sizeof(int))); r.shift(); - EXPECT_TRUE(r.accum(sizeof(int))); - EXPECT_TRUE(r.accum(sizeof(int))); - EXPECT_FALSE(r.accum(sizeof(int))); + EXPECT_EQ(0, r.accum(sizeof(int))); + EXPECT_EQ(0, r.accum(sizeof(int))); + EXPECT_EQ(3, r.accum(sizeof(int))); } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <yan...@us...> - 2009-03-07 21:18:29
|
Revision: 1267 http://assorted.svn.sourceforge.net/assorted/?rev=1267&view=rev Author: yangzhang Date: 2009-03-07 21:18:08 +0000 (Sat, 07 Mar 2009) Log Message: ----------- - added iterators and a bunch of other standards-conforming features to fast_map - added fast_map tests Modified Paths: -------------- cpp-commons/trunk/src/commons/fast_map.h Added Paths: ----------- cpp-commons/trunk/src/test/fast_map.cc Modified: cpp-commons/trunk/src/commons/fast_map.h =================================================================== --- cpp-commons/trunk/src/commons/fast_map.h 2009-03-07 21:15:20 UTC (rev 1266) +++ cpp-commons/trunk/src/commons/fast_map.h 2009-03-07 21:18:08 UTC (rev 1267) @@ -1,16 +1,120 @@ #ifndef COMMONS_DENSE_HASH_MAP_H #define COMMONS_DENSE_HASH_MAP_H -#include <backward/hash_fun.h> +#include <boost/functional/hash.hpp> #include <cassert> #include <commons/array.h> +#include <commons/exceptions.h> #include <utility> namespace commons { + using namespace boost; using namespace std; - using namespace __gnu_cxx; + // TODO write unit tests + + template<typename Key, typename Data> class fast_map; + template<typename map_traits> class fast_map_iterator; + template<typename map_traits> class fast_map_const_iterator; + + template<typename Key, typename Data> + struct fast_map_traits + { + typedef fast_map<Key, Data> map_type; + typedef Key key_type; + typedef Data data_type; + typedef pair<key_type, data_type> value_type; + typedef fast_map_traits<Key, Data> traits; + + typedef value_type &reference; + typedef const value_type &const_reference; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef fast_map_iterator<traits> iterator; + typedef fast_map_const_iterator<traits> const_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + }; + + template<typename map_traits> + class fast_map_const_iterator + { + public: + typedef typename map_traits::map_type map_type; + typedef typename map_traits::iterator iterator; + typedef typename map_traits::const_iterator const_iterator; + typedef typename map_traits::value_type value_type; + typedef typename map_traits::reference reference; + typedef typename map_traits::const_reference const_reference; + typedef typename map_traits::pointer pointer; + typedef typename map_traits::const_pointer const_pointer; + typedef typename map_traits::size_type size_type; + typedef typename map_traits::difference_type difference_type; + typedef forward_iterator_tag iterator_category; + private: + const map_type &m; + const value_type *p; + const value_type *end; + public: + fast_map_const_iterator(const map_type &m, const value_type *p) + : m(m), p(p), end(m.get_table().end()) { increment(); } + void increment() { for (; p != end && m.empty_or_deleted(p->first); ++p) {} } + const_iterator &operator++() { + ++p; + increment(); + return *this; + } + const_iterator operator++(int) { + fast_map_const_iterator copy = *this; + ++*this; + return copy; + } + const_reference operator*() const { return *p; } + const_pointer operator->() const { return p; } + bool operator==(const const_iterator &it) const { return p == it.p; } + bool operator!=(const const_iterator &it) const { return p != it.p; } + }; + + template<typename map_traits> + class fast_map_iterator + { + public: + typedef typename map_traits::map_type map_type; + typedef typename map_traits::iterator iterator; + typedef typename map_traits::const_iterator const_iterator; + typedef typename map_traits::value_type value_type; + typedef typename map_traits::reference reference; + typedef typename map_traits::const_reference const_reference; + typedef typename map_traits::pointer pointer; + typedef typename map_traits::const_pointer const_pointer; + typedef typename map_traits::size_type size_type; + typedef typename map_traits::difference_type difference_type; + typedef forward_iterator_tag iterator_category; + private: + map_type &m; + value_type *p; + value_type *end; + public: + fast_map_iterator(map_type &m, value_type *p) + : m(m), p(p), end(m.get_table().end()) { increment(); } + void increment() { for (; p != end && m.empty_or_deleted(p->first); ++p) {} } + iterator &operator++() { + ++p; + increment(); + return *this; + } + iterator operator++(int) { + fast_map_iterator copy = *this; + ++*this; + return copy; + } + reference operator*() const { return *p; } + pointer operator->() const { return p; } + bool operator==(const iterator &it) const { return p == it.p; } + bool operator!=(const iterator &it) const { return p != it.p; } + }; + /** * Quadratic internal probing with triangular numbers, a la * google::dense_hash_map. Took the overall design from @@ -24,66 +128,147 @@ * ability to serialize just a certain range of the fast_map is also * important. */ - // TODO someday: template <typename K, typename V> + template <typename Key, typename Data> class fast_map { + public: + typedef fast_map_traits<Key, Data> traits; + typedef typename traits::map_type map_type; + typedef typename traits::iterator iterator; + typedef typename traits::const_iterator const_iterator; + typedef typename traits::key_type key_type; + typedef typename traits::data_type data_type; + typedef typename traits::value_type value_type; + typedef typename traits::reference reference; + typedef typename traits::const_reference const_reference; + typedef typename traits::pointer pointer; + typedef typename traits::const_pointer const_pointer; + private: - static const size_t initsize = 1<<20; - typedef pair<int, int> entry; - commons::array<entry> table; - int empty_key, deleted_key; + static const size_t initsize = 1 << 20; + commons::array<value_type> table; + key_type empty_key, deleted_key; size_t count; - hash<int> hasher; + boost::hash<key_type> hasher; + bool has_empty_key, has_deleted_key; + void resize(size_t size) { - commons::array<entry> tmp(size); - // TODO faster? + commons::array<value_type> newtab(0); + newtab.reset(reinterpret_cast<value_type*>(new char[size * sizeof(value_type)]), size); for (size_t i = 0; i < size; ++i) - tmp[i].first = empty_key; - swap(table, tmp); + newtab[i].first = empty_key; // Rehash old values over into new table. - size_t mask = table.size() - 1; - for (size_t i = 0; i < tmp.size(); ++i) { - if (tmp[i].first != empty_key && tmp[i].first != deleted_key) { + size_t mask = newtab.size() - 1; + for (size_t i = 0; i < table.size(); ++i) { + if (table[i].first != empty_key && table[i].first != deleted_key) { // Don't want to simply reuse the general lookup, since we know // we're starting with a blank slate. - size_t pos = hasher(tmp[i].first) & mask; + size_t pos = hasher(table[i].first) & mask; for (size_t probe = 0; - table[pos].first != empty_key; - ++probe, pos = (pos + probe) & mask) { - assert(probe < table.size()); + newtab[pos].first != empty_key; + pos = (pos + ++probe) & mask) { + assert(probe < newtab.size()); } - table[pos] = tmp[pos]; + newtab[pos] = table[pos]; } } + swap(newtab, table); } + void grow() { resize(table.size() << 1); } void shrink() { resize(table.size() >> 1); } + void assert_init() const { assert(has_empty_key && has_deleted_key); } + public: - fast_map() : table(0), empty_key(0), deleted_key(0), count(0) {} - void set_empty_key(int k) { empty_key = k; resize(initsize); } - void set_deleted_key(int k) { deleted_key = k; resize(initsize); } + fast_map() : + table(0), empty_key(0), deleted_key(0), count(0), has_empty_key(false), + has_deleted_key(false) {} + void set_empty_key(key_type k) { + has_empty_key = true; + empty_key = k; + resize(initsize); + } + void set_deleted_key(key_type k) { + has_deleted_key = true; + deleted_key = k; + resize(initsize); + } size_t size() const { return count; } - int &operator[](int k) { - size_t mask = table.size() - 1; - size_t probe, pos = hasher(k) & mask; // , unset = -1, first_deleted = unset; + void erase(iterator) { throw_not_implemented(); } + array<value_type> &get_table() { return table; } + const array<value_type> &get_table() const { return table; } + bool empty_or_deleted(key_type k) const { return k == empty_key || k == deleted_key; } + + iterator begin() { + assert_init(); + if (count == 0) return end(); + else return iterator(*this, table.begin()); + } + const_iterator begin() const { + assert_init(); + if (count == 0) return end(); + else return const_iterator(*this, table.begin()); + } + + iterator end() { + assert_init(); + return iterator(*this, table.end()); + } + const_iterator end() const { + assert_init(); + return const_iterator(*this, table.end()); + } + + const_iterator find(key_type k) const { + assert_init(); + size_t probe = 0, mask = table.size() - 1, pos = hasher(k) & mask; + while (true) { + if (table[pos].first == empty_key) return end(); + if (table[pos].first == k) return const_iterator(*this, &table[pos]); + pos = (pos + ++probe) & mask; + } + } + + iterator find(key_type k) { + assert_init(); + size_t probe = 0, mask = table.size() - 1, pos = hasher(k) & mask; + while (true) { + if (table[pos].first == empty_key) return end(); + if (table[pos].first == k) return iterator(*this, &table[pos]); + pos = (pos + ++probe) & mask; + } +#if 0 + for (; + table[pos].first != empty_key && table[pos].first != k; + pos = (pos + ++probe) & mask) { + assert(probe < table.size()); + } + if (table[pos].first == empty_key) return end(); + else return &table[pos]; +#endif + } + + data_type &operator[](key_type k) { + assert_init(); + size_t probe = 0, mask = table.size() - 1, pos = hasher(k) & mask; // Lookup. Skip over deleted entries (but remembering the first one // seen as an open slot if we later decide to insert). If we find an // empty spot, try returning the earlier deleted spot first. If we // find the key, return that. - for (probe = 0; + for (; table[pos].first != deleted_key && table[pos].first != empty_key && table[pos].first != k; - ++probe, pos = (pos + probe) & mask) { + pos = (pos + ++probe) & mask) { assert(probe < table.size()); } if (table[pos].first == deleted_key) { size_t first_deleted = pos; for (; table[pos].first != empty_key && table[pos].first != k; - ++probe, pos = (pos + probe) & mask) { + pos = (pos + ++probe) & mask) { assert(probe < table.size()); } if (table[pos].first == empty_key) { - // Inserting new entry. Grow table if necessary. + // Inserting new value_type. Grow table if necessary. if (++count > table.size() * 3 / 4) { --count; grow(); @@ -93,7 +278,7 @@ table[pos].first = k; } } else if (table[pos].first == empty_key) { - // Inserting new entry. Grow table if necessary. + // Inserting new value_type. Grow table if necessary. if (++count > table.size() * 3 / 4) { --count; grow(); Added: cpp-commons/trunk/src/test/fast_map.cc =================================================================== --- cpp-commons/trunk/src/test/fast_map.cc (rev 0) +++ cpp-commons/trunk/src/test/fast_map.cc 2009-03-07 21:18:08 UTC (rev 1267) @@ -0,0 +1,83 @@ +#include <commons/fast_map.h> +#include <boost/foreach.hpp> +#include "test.h" + +static const char *unset_key_string = "Assertion `has_empty_key && has_deleted_key' failed."; + +template<typename T> +void const_uninit_tests(T &fm) { + ASSERT_DEATH(fm.begin(), unset_key_string); + ASSERT_DEATH(fm.end(), unset_key_string); + ASSERT_DEATH(fm.find(0), unset_key_string); +} + +template<typename T> +void const_empty_tests(T &fm) { + EXPECT_EQ(0, fm.size()); + EXPECT_TRUE(fm.begin() == fm.end()); + EXPECT_TRUE(fm.find(0) == fm.end()); + EXPECT_FALSE(fm.begin() != fm.end()); + EXPECT_FALSE(fm.find(0) != fm.end()); +} + +template<typename T> +void const_nonempty_tests(T &fm) { + EXPECT_EQ(10, fm.size()); + EXPECT_TRUE(fm.begin() != fm.end()); + EXPECT_TRUE(fm.find(0) != fm.end()); + EXPECT_FALSE(fm.begin() == fm.end()); + EXPECT_FALSE(fm.find(0) == fm.end()); + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(2*i, fm.find(2*i)->first); + EXPECT_EQ(2*i, (*fm.find(2*i)).first); + EXPECT_EQ(4*i, fm.find(2*i)->second); + EXPECT_EQ(4*i, (*fm.find(2*i)).second); + } + EXPECT_TRUE(fm.begin() == fm.find(0)); + EXPECT_TRUE(fm.end() == fm.find(1)); + + typedef pair<int, int> pii; + size_t counter = 0; + vector<int> xs(20); + for (typeof(fm.begin()) it = fm.begin(); + it != fm.end(); + ++it) { + const pii &p = *it; + ++counter; + xs[p.first] = p.second; + } + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(4 * i, xs[2 * i]); + } + EXPECT_EQ(10, counter); +} + +TEST(fast_map, basics) { + typedef fast_map<int, int> map_t; + map_t fm; + + // Uninitialized tests. + const_uninit_tests(fm); + const_uninit_tests<const map_t>(fm); + ASSERT_DEATH(fm[0], unset_key_string); + + // Initialize, leave empty. + fm.set_empty_key(-1); + fm.set_deleted_key(-2); + + // Empty tests. + const_empty_tests(fm); + const_empty_tests<const map_t>(fm); + + // Fill. + for (int i = 0; i < 10; ++i) { + fm[2*i] = 4*i; + } + + // Filled tests. + const_nonempty_tests(fm); + const_nonempty_tests<const map_t>(fm); + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(4*i, fm[2*i]); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <yan...@us...> - 2009-03-07 21:30:43
|
Revision: 1268 http://assorted.svn.sourceforge.net/assorted/?rev=1268&view=rev Author: yangzhang Date: 2009-03-07 21:30:30 +0000 (Sat, 07 Mar 2009) Log Message: ----------- - added some load tests for fast_map - fixed an index-out-of-bounds bug, using incorrect index into old table when growing Modified Paths: -------------- cpp-commons/trunk/src/commons/fast_map.h cpp-commons/trunk/src/test/fast_map.cc Modified: cpp-commons/trunk/src/commons/fast_map.h =================================================================== --- cpp-commons/trunk/src/commons/fast_map.h 2009-03-07 21:18:08 UTC (rev 1267) +++ cpp-commons/trunk/src/commons/fast_map.h 2009-03-07 21:30:30 UTC (rev 1268) @@ -169,7 +169,7 @@ pos = (pos + ++probe) & mask) { assert(probe < newtab.size()); } - newtab[pos] = table[pos]; + newtab[pos] = table[i]; } } swap(newtab, table); Modified: cpp-commons/trunk/src/test/fast_map.cc =================================================================== --- cpp-commons/trunk/src/test/fast_map.cc 2009-03-07 21:18:08 UTC (rev 1267) +++ cpp-commons/trunk/src/test/fast_map.cc 2009-03-07 21:30:30 UTC (rev 1268) @@ -1,8 +1,13 @@ #include <commons/fast_map.h> +#include <commons/rand.h> #include <boost/foreach.hpp> +#include <map> #include "test.h" +using namespace std; static const char *unset_key_string = "Assertion `has_empty_key && has_deleted_key' failed."; +typedef fast_map<int, int> map_t; +typedef pair<int, int> pii; template<typename T> void const_uninit_tests(T &fm) { @@ -36,7 +41,6 @@ EXPECT_TRUE(fm.begin() == fm.find(0)); EXPECT_TRUE(fm.end() == fm.find(1)); - typedef pair<int, int> pii; size_t counter = 0; vector<int> xs(20); for (typeof(fm.begin()) it = fm.begin(); @@ -53,7 +57,6 @@ } TEST(fast_map, basics) { - typedef fast_map<int, int> map_t; map_t fm; // Uninitialized tests. @@ -81,3 +84,31 @@ EXPECT_EQ(4*i, fm[2*i]); } } + +TEST(fast_map, randload) { + map_t fm; + map<int, int> m; + fm.set_empty_key(-1); + fm.set_deleted_key(-2); + for (int i = 0; i < 1<<22; ++i) { + int k = randint(); + if (fm.find(k) == fm.end()) { + int v = randint(); + fm[k] = v; + m[k] = v; + } + } + foreach (const pii &p, m) { + EXPECT_EQ(p.second, fm[p.first]); + } +} + +TEST(fast_map, load) { + map_t m; + m.set_empty_key(-1); + m.set_deleted_key(-2); + for (int i = 0; i < 1<<22; ++i) + m[i] = i<<1; + for (int i = 0; i < 1<<22; ++i) + EXPECT_EQ(i<<1, m[i]); +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <yan...@us...> - 2009-04-30 15:57:03
|
Revision: 1358 http://assorted.svn.sourceforge.net/assorted/?rev=1358&view=rev Author: yangzhang Date: 2009-04-30 15:56:53 +0000 (Thu, 30 Apr 2009) Log Message: ----------- added serialization/deserialization to versioned_heap; added nbits2nints, getbit, setbit Modified Paths: -------------- cpp-commons/trunk/src/commons/memory.h cpp-commons/trunk/src/commons/versioned_heap.h cpp-commons/trunk/src/test/Makefile cpp-commons/trunk/src/test/versioned_heap.cc Modified: cpp-commons/trunk/src/commons/memory.h =================================================================== --- cpp-commons/trunk/src/commons/memory.h 2009-04-29 20:45:50 UTC (rev 1357) +++ cpp-commons/trunk/src/commons/memory.h 2009-04-30 15:56:53 UTC (rev 1358) @@ -61,6 +61,25 @@ void skip(size_t n) { p_ += n; } }; + size_t nbits2nints(size_t nbits) { + return (nbits + 31) / 32; + } + + template<typename T> + bool getbit(T x, size_t bit) { + return (x >> bit) & 1; + } + + template<typename T> + T setbit(T x, size_t bit) { + return x | (1U << bit); + } + + template<typename T> + T setbit(T x, size_t bit, T val) { + return x | (val << bit); + } + } #endif Modified: cpp-commons/trunk/src/commons/versioned_heap.h =================================================================== --- cpp-commons/trunk/src/commons/versioned_heap.h 2009-04-29 20:45:50 UTC (rev 1357) +++ cpp-commons/trunk/src/commons/versioned_heap.h 2009-04-30 15:56:53 UTC (rev 1358) @@ -5,6 +5,7 @@ #include <cstdlib> #include <vector> #include <boost/foreach.hpp> +#include <commons/memory.h> #define foreach BOOST_FOREACH namespace commons @@ -25,42 +26,69 @@ struct hdr { version_type version; uint32_t index; + bool managed; }; versioned_heap(size_t pgsz = 131072) : - pgsz_(pgsz), pgcnt_((pgsz - sizeof(hdr)) / sizeof(T)) {} + last_i_(0), pgsz_(pgsz), pgcnt_((pgsz - sizeof(hdr)) / sizeof(T)) {} + /** Frees all pages, without destroying the objects. */ ~versioned_heap() { - foreach (char *page, pages_) ::free(page); + foreach (char *page, pages_) + if (hdrof(page).managed) + ::free(page); } - T &construct(version_type version = 0) { - for (size_t i = 0; i < freemap_.size(); ++i) { + char *alloc() { + for (size_t counter = 1; counter <= freemap_.size(); ++counter) { + size_t i = (last_i_ + counter) % freemap_.size(); if (freemap_[i]) { freemap_[i] = false; char *pos = pages_[i / pgcnt_] + sizeof(hdr) + (i % pgcnt_) * sizeof(T); - if (version == 0) ++hdrof(pos).version; - else hdrof(pos).version = version; - return *new (pos) T; + ++hdrof(pos).version; + last_i_ = i; + return pos; } } char *page; posix_memalign(reinterpret_cast<void**>(&page), pgsz_, pgsz_); hdr &h = *reinterpret_cast<hdr*>(page); - h.version = version; + h.version = 0; h.index = uint32_t(pages_.size()); + h.managed = true; pages_.push_back(page); freemap_.resize(freemap_.size() + pgcnt_, true); freemap_[freemap_.size() - pgcnt_] = false; - return *new (page + sizeof(hdr)) T; + return page + sizeof(hdr); } - void free(T &x, version_type version = 0) { - x.~T(); - hdr &h = hdrof(&x); + T &construct() { + char *buf = alloc(); + try { return *new (buf) T; } + catch (...) { free(buf); throw; } + } + // TODO: replace with code gen + template<typename A0> + T &construct(const A0 &a0) { + char *buf = alloc(); + try { return *new (buf) T(a0); } + catch (...) { free(buf); throw; } + } + template<typename A0, typename A1> + T &construct(const A0 &a0, const A1 &a1) { + char *buf = alloc(); + try { return *new (buf) T(a0, a1); } + catch (...) { free(buf); throw; } + } + void free(void *p, version_type version = 0) { + hdr &h = hdrof(p); if (version == 0) ++h.version; else h.version = version; - char *px = reinterpret_cast<char*>(&x); + char *px = reinterpret_cast<char*>(p); char *p0 = reinterpret_cast<char*>(&h + 1); freemap_[h.index * pgcnt_ + (px - p0) / sizeof(T)] = true; } + void destroy(T &x, version_type version = 0) { + x.~T(); + free(&x, version); + } void touch(T &p) { ++hdrof(&p).version; } void touch(T &p, version_type version) { hdrof(&p).version = version; } size_t pgcnt() const { return pgcnt_; } @@ -73,13 +101,115 @@ hdr &hdrof(void *p) { return *reinterpret_cast<hdr*>(uintptr_t(p) & ~(pgsz_ - 1)); } + + /** + * Calculate the required space for serializing the metadata. + */ + size_t metasize() const { + return sizeof last_i_ + sizeof pgsz_ + sizeof pgcnt_ + + sizeof freemap_.size() + + nbits2nints(freemap_.size()) * sizeof(uint32_t) + + sizeof datasize(*this); + } + + /** + * Calculate the required space for serializing the metadata and data. + */ + size_t sersize() const { + return metasize() + datasize(*this); + } + + /** + * Serializes metadata and data to out buffer. + */ + void ser(void *meta, void *data) const { + raw_writer w(meta); + + // Serialize metadata. + w.write(last_i_); + w.write(pgsz_); + w.write(pgcnt_); + + // Serialize freemap. + w.write(freemap_.size()); + size_t freesize = nbits2nints(freemap_.size()); + for (size_t i = 0; i < freesize; ++i) { + uint32_t val = 0; + for (size_t j = 0; j < 32 && 32 * i + j < freemap_.size(); ++j) { + val = setbit(val, j, uint32_t(freemap_[32 * i + j])); + } + w.write(val); + } + + // Serialize data. + w.write(datasize(*this)); + serdata(*this, data); + } + + /** + * Deserializes a heap in-place. + */ + void deser(void *meta, void *data) { + raw_reader r(meta); + + // Deserialize metadata. + r.read(last_i_); + r.read(pgsz_); + r.read(pgcnt_); + + // Deserialize freemap. + size_t nbits; + r.read(nbits); + size_t nints = nbits2nints(nbits); + for (size_t i = 0; i < nints; ++i) { + uint32_t val; + r.read(val); + for (size_t j = 0; j < nbits && 32 * i + j < nbits; ++j) { + freemap_.push_back(getbit(val, j)); + } + } + + // Deserialize data. + size_t datasize; + r.read(datasize); + char *p0 = reinterpret_cast<char*>(data); + for (char *p = p0; p < p0 + datasize; p += pgsz_) { + hdrof(p).managed = false; + pages_.push_back(p); + } + } + private: + size_t last_i_; size_t pgsz_; size_t pgcnt_; vector<bool> freemap_; vector<char*> pages_; }; + /** + * Calculate the data size of a heap. + */ + template<typename T> + size_t datasize(const versioned_heap<T> &h) + { + return h.pages().size() * h.pgsz(); + } + + /** + * Copies pages from the heap into the out buffer. Assumes out is big enough + * (see datasize). + */ + template<typename T> + void serdata(const versioned_heap<T> &h, void *out) + { + char *p = reinterpret_cast<char*>(out); + foreach (void *page, h.pages()) { + memcpy(p, page, h.pgsz()); + p += h.pgsz(); + } + } + } #undef foreach Modified: cpp-commons/trunk/src/test/Makefile =================================================================== --- cpp-commons/trunk/src/test/Makefile 2009-04-29 20:45:50 UTC (rev 1357) +++ cpp-commons/trunk/src/test/Makefile 2009-04-30 15:56:53 UTC (rev 1358) @@ -1,4 +1,5 @@ CXXFLAGS = \ + -MD \ -g3 \ -Wall \ -Werror \ @@ -46,3 +47,5 @@ squeue: LDLIBS += -lboost_thread-gcc43-mt .PHONY: all build clean + +-include *.d Modified: cpp-commons/trunk/src/test/versioned_heap.cc =================================================================== --- cpp-commons/trunk/src/test/versioned_heap.cc 2009-04-29 20:45:50 UTC (rev 1357) +++ cpp-commons/trunk/src/test/versioned_heap.cc 2009-04-30 15:56:53 UTC (rev 1358) @@ -1,3 +1,5 @@ +#include <commons/check.h> +#include <commons/unique_ptr.h> #include <commons/versioned_heap.h> #include "test.h" @@ -4,30 +6,31 @@ struct s { char xs[40000]; }; TEST(versioned_heap, basics) { - versioned_heap<s> h; + typedef versioned_heap<s> heap; + heap h; s &a = h.construct(); - EXPECT_EQ(h.hdrof(&a).version, 0); + EXPECT_EQ(0, h.hdrof(&a).version); s &b = h.construct(); - EXPECT_EQ(h.hdrof(&b).version, 1); + EXPECT_EQ(1, h.hdrof(&b).version); s &c = h.construct(); - EXPECT_EQ(h.hdrof(&c).version, 2); + EXPECT_EQ(2, h.hdrof(&c).version); EXPECT_EQ(&a + 1, &b); EXPECT_EQ(&b + 1, &c); s &d = h.construct(); - EXPECT_EQ(h.hdrof(&d).version, 0); + EXPECT_EQ(0, h.hdrof(&d).version); s &e = h.construct(); - EXPECT_EQ(h.hdrof(&e).version, 1); + EXPECT_EQ(1, h.hdrof(&e).version); s &f = h.construct(); - EXPECT_EQ(h.hdrof(&f).version, 2); + EXPECT_EQ(2, h.hdrof(&f).version); EXPECT_EQ(&d + 1, &e); EXPECT_EQ(&e + 1, &f); h.touch(d); EXPECT_EQ(h.hdrof(&d).version, 3); - h.free(d); + h.destroy(d); EXPECT_EQ(h.hdrof(&d).version, 4); h.touch(a, 4); @@ -37,4 +40,22 @@ s &g = h.construct(); EXPECT_EQ(&d, &g); + + void *meta = new char[h.metasize()], *data; + check0x(posix_memalign(&data, h.pgsz(), datasize(h))); + unique_ptr<char[]> umeta(reinterpret_cast<char*>(meta)), + udata(reinterpret_cast<char*>(data)); + h.ser(meta, data); + + heap h2; + h2.deser(meta, data); + + EXPECT_EQ(h.pages().size(), h2.pages().size()); + for (size_t i = 0; i < h.pages().size(); ++i) { + s *p = reinterpret_cast<s*>(h .pages()[i] + sizeof(heap::hdr)); + s *q = reinterpret_cast<s*>(h2.pages()[i] + sizeof(heap::hdr)); + for (size_t j = 0; j < h.pgcnt(); ++j) { + EXPECT_EQ(0, memcmp(p, q, sizeof(s))); + } + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <yan...@us...> - 2009-04-30 20:39:21
|
Revision: 1359 http://assorted.svn.sourceforge.net/assorted/?rev=1359&view=rev Author: yangzhang Date: 2009-04-30 20:39:12 +0000 (Thu, 30 Apr 2009) Log Message: ----------- renamed getbit, setbit to get_bit, set_bit; added whole_units Modified Paths: -------------- cpp-commons/trunk/src/commons/memory.h cpp-commons/trunk/src/commons/versioned_heap.h Added Paths: ----------- cpp-commons/trunk/src/test/memory.cc Modified: cpp-commons/trunk/src/commons/memory.h =================================================================== --- cpp-commons/trunk/src/commons/memory.h 2009-04-30 15:56:53 UTC (rev 1358) +++ cpp-commons/trunk/src/commons/memory.h 2009-04-30 20:39:12 UTC (rev 1359) @@ -2,6 +2,7 @@ #define COMMONS_MEMORY_H #include <cstring> // for size_t +#include <commons/utility.h> // for UNUSED namespace commons { @@ -61,22 +62,32 @@ void skip(size_t n) { p_ += n; } }; - size_t nbits2nints(size_t nbits) { + /** Round `value` up to the nearest multiple of `unit` and return this + * factor. */ + template<typename T> + T whole_units(T value, T unit) { + return (value + unit - 1) / unit; + } + + /** Number of 32-bit ints to hold the given number of bits. */ + UNUSED static size_t nbits2nints(size_t nbits) { return (nbits + 31) / 32; } template<typename T> - bool getbit(T x, size_t bit) { + bool get_bit(T x, size_t bit) { return (x >> bit) & 1; } + /** Note: there exists a setbit macro in /usr/include/sys/param.h */ template<typename T> - T setbit(T x, size_t bit) { + T set_bit(T x, size_t bit) { return x | (1U << bit); } + /** Note: there exists a setbit macro in /usr/include/sys/param.h */ template<typename T> - T setbit(T x, size_t bit, T val) { + T set_bit(T x, size_t bit, T val) { return x | (val << bit); } Modified: cpp-commons/trunk/src/commons/versioned_heap.h =================================================================== --- cpp-commons/trunk/src/commons/versioned_heap.h 2009-04-30 15:56:53 UTC (rev 1358) +++ cpp-commons/trunk/src/commons/versioned_heap.h 2009-04-30 20:39:12 UTC (rev 1359) @@ -1,5 +1,5 @@ -#ifndef VERSIONED_HEAP_H -#define VERSIONED_HEAP_H +#ifndef COMMONS_VERSIONED_HEAP_H +#define COMMONS_VERSIONED_HEAP_H #define _POSIX_C_SOURCE 200112L #include <cstdlib> @@ -136,7 +136,7 @@ for (size_t i = 0; i < freesize; ++i) { uint32_t val = 0; for (size_t j = 0; j < 32 && 32 * i + j < freemap_.size(); ++j) { - val = setbit(val, j, uint32_t(freemap_[32 * i + j])); + val = set_bit(val, j, uint32_t(freemap_[32 * i + j])); } w.write(val); } @@ -165,7 +165,7 @@ uint32_t val; r.read(val); for (size_t j = 0; j < nbits && 32 * i + j < nbits; ++j) { - freemap_.push_back(getbit(val, j)); + freemap_.push_back(get_bit(val, j)); } } Added: cpp-commons/trunk/src/test/memory.cc =================================================================== --- cpp-commons/trunk/src/test/memory.cc (rev 0) +++ cpp-commons/trunk/src/test/memory.cc 2009-04-30 20:39:12 UTC (rev 1359) @@ -0,0 +1,13 @@ +#include <commons/memory.h> +#include "test.h" + +TEST(memory, whole_units) { + EXPECT_EQ(0, whole_units(0, 100)); + EXPECT_EQ(1, whole_units(1, 100)); + EXPECT_EQ(1, whole_units(2, 100)); + EXPECT_EQ(1, whole_units(98, 100)); + EXPECT_EQ(1, whole_units(99, 100)); + EXPECT_EQ(1, whole_units(100, 100)); + EXPECT_EQ(2, whole_units(101, 100)); + EXPECT_EQ(2, whole_units(102, 100)); +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |