00001
00002
00003
00004
00005
00006
00007
00008
00009 #ifndef ADOBE_COPY_ON_WRITE_HPP
00010 #define ADOBE_COPY_ON_WRITE_HPP
00011
00012
00013
00014 #include <adobe/config.hpp>
00015
00016 #include <algorithm>
00017 #include <cassert>
00018 #include <cstdlib>
00019
00020 #include <boost/noncopyable.hpp>
00021 #include <boost/operators.hpp>
00022 #include <boost/static_assert.hpp>
00023
00024 #include <adobe/counter.hpp>
00025 #include <adobe/memory.hpp>
00026 #include <adobe/move.hpp>
00027 #include <adobe/once.hpp>
00028 #include <adobe/typeinfo.hpp>
00029
00030
00031
00032 namespace adobe {
00033
00034
00035
00036 namespace version_1 {
00037
00038
00039
00069 template <typename T,
00070 typename A = adobe::capture_allocator<T> >
00071 class copy_on_write
00072 {
00073 public:
00075 typedef T value_type;
00077 typedef A allocator_type;
00078
00079 #if !defined(ADOBE_NO_DOCUMENTATION)
00080 private:
00081 struct implementation_t;
00082 typedef typename allocator_type::template rebind<implementation_t>::other implementation_allocator_type;
00083 public:
00084 #endif
00085
00091 copy_on_write()
00092 {
00093 adobe::call_once(init_default, flag_s);
00094 object_m = default_s;
00095 object_m->header_m.get().count_m.increment();
00096 }
00097
00101 explicit copy_on_write(const allocator_type& a) :
00102 object_m(0)
00103 {
00104 implementation_allocator_type other_allocator(a);
00105
00106 object_m = allocate(other_allocator);
00107 }
00108
00116 template <typename U>
00117 copy_on_write(U x, typename move_sink<U, T>::type = 0) :
00118 object_m(0)
00119 {
00120
00121
00122
00123
00124
00125 object_m = allocate_move(0, move(x));
00126 }
00127
00135 template <typename U>
00136 copy_on_write(const U& x, typename copy_sink<U, T>::type = 0) :
00137 object_m(0)
00138 {
00139
00140
00141
00142
00143
00144 object_m = allocate(static_cast<implementation_t*>(0), x);
00145 }
00146
00151 copy_on_write(const copy_on_write& x) :
00152 object_m(x.object_m)
00153 {
00154 if (object_m)
00155 object_m->header_m.get().count_m.increment();
00156 }
00157
00158 copy_on_write(move_from<copy_on_write> x) :
00159 object_m(x.source.object_m)
00160 {
00161 x.source.object_m = 0;
00162 }
00163
00164 ~copy_on_write()
00165 {
00166 release(object_m);
00167 }
00168
00174 copy_on_write& operator=(copy_on_write x)
00175 { swap(*this, x); return *this; }
00176
00177
00178 template<typename U>
00179 typename move_sink<U, T, copy_on_write&>::type operator=(U x)
00180 {
00181 if (!object_m)
00182 object_m = allocate_move(0, move(x));
00183 else if (object_m->header_m.get().count_m.is_one())
00184 object_m->value_m = move(x);
00185 else
00186 reset(allocate_move(object_m, move(x)));
00187
00188 return *this;
00189 }
00190
00191
00192
00193
00194
00199 template<typename U>
00200 typename copy_sink<U, T, copy_on_write&>::type operator=(const U& x)
00201 {
00202 if (!object_m)
00203 object_m = allocate(0, x);
00204 else if (object_m->header_m.get().count_m.is_one())
00205 object_m->value_m = x;
00206 else
00207 reset(allocate(object_m, x));
00208
00209 return *this;
00210 }
00211
00224 value_type& write()
00225 {
00226 assert(object_m && "FATAL (sparent) : using a moved copy_on_write object");
00227
00228 if (!object_m->header_m.get().count_m.is_one())
00229 reset(allocate(object_m, object_m->value_m));
00230
00231 return object_m->value_m;
00232 }
00233
00239 const value_type& read() const
00240 {
00241 assert(object_m && "FATAL (sparent) : using a moved copy_on_write object");
00242 return object_m->value_m;
00243 }
00244
00250 operator const value_type& () const
00251 { return read(); }
00252
00263 const value_type& operator*() const
00264 { return read(); }
00265
00276 const value_type* operator->() const
00277 { return &read(); }
00278
00286 bool unique_instance() const
00287 { return !object_m || object_m->header_m.get().count_m.is_one(); }
00288
00296 bool identity(const copy_on_write& x) const
00297 { return object_m == x.object_m; }
00298
00299 friend inline void swap(copy_on_write& x, copy_on_write& y)
00300 { std::swap(x.object_m, y.object_m); }
00301
00302 friend inline bool operator<(const copy_on_write& x, const copy_on_write& y)
00303 { return y.object_m && (!x.object_m || (!x.identity(y) && *x < *y)); }
00304
00305 friend inline bool operator>(const copy_on_write& x, const copy_on_write& y)
00306 { return y < x; }
00307
00308 friend inline bool operator<=(const copy_on_write& x, const copy_on_write& y)
00309 { return !(y < x); }
00310
00311 friend inline bool operator>=(const copy_on_write& x, const copy_on_write& y)
00312 { return !(x < y); }
00313
00314 friend inline bool operator==(const copy_on_write& x, const copy_on_write& y)
00315 { return x.identity(y) || (x.object_m && y.object_m && *x == *y); }
00316
00317 friend inline bool operator!=(const copy_on_write& x, const copy_on_write& y)
00318 { return !(x == y); }
00319
00320 allocator_type get_allocator() const
00321 { return object_m ? allocator_type(object_m->get_allocator()) : allocator_type(); }
00322
00323 private:
00324 #if !defined(ADOBE_NO_DOCUMENTATION)
00325 static implementation_t* allocate(const implementation_t* alloc_src, const T& x = T())
00326 {
00327 implementation_allocator_type allocator(alloc_src ?
00328 alloc_src->get_allocator() :
00329 implementation_allocator_type());
00330
00331 return allocate(allocator, x);
00332 }
00333
00334 static implementation_t* allocate(implementation_allocator_type& allocator, const T& x = T())
00335 {
00336 implementation_t* tmp(allocator.allocate(1));
00337
00338 try {
00339 ::new(static_cast<void*>(tmp)) implementation_t(x);
00340 } catch (...) {
00341 tmp->get_allocator().deallocate(tmp, 1);
00342 throw;
00343 }
00344
00345 return tmp;
00346 }
00347
00348 template <typename U>
00349 static implementation_t* allocate_move(const implementation_t* alloc_src, U x)
00350 {
00351 implementation_allocator_type allocator(alloc_src ?
00352 alloc_src->get_allocator() :
00353 implementation_allocator_type());
00354 implementation_t* tmp(allocator.allocate(1));
00355
00356 try {
00357 ::new(static_cast<void*>(tmp)) implementation_t(move_from<U>(x));
00358 } catch (...) {
00359 tmp->get_allocator().deallocate(tmp, 1);
00360 throw;
00361 }
00362
00363 return tmp;
00364 }
00365
00366 static void release(implementation_t* x)
00367 {
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377 if (x == 0 || x->header_m.get().count_m.decrement() == false)
00378 return;
00379
00380 implementation_allocator_type allocator(x->get_allocator());
00381
00382 destroy(x);
00383
00384 allocator.deallocate(x, 1);
00385 }
00386
00387 void reset(implementation_t* to)
00388 {
00389 release(object_m);
00390 object_m = to;
00391 }
00392
00393 implementation_t* object_m;
00394
00395 static once_flag flag_s;
00396 static implementation_t* default_s;
00397
00398 static void release_default();
00399 static void init_default();
00400 #endif
00401 };
00402
00403
00404
00405 #if !defined(ADOBE_NO_DOCUMENTATION)
00406
00407
00408
00409 template <typename T, typename A>
00410 once_flag copy_on_write<T, A>::flag_s = ADOBE_ONCE_INIT;
00411
00412 template <typename T, typename A>
00413 typename copy_on_write<T, A>::implementation_t* copy_on_write<T, A>::default_s;
00414
00415 template <typename T, typename A>
00416 struct copy_on_write<T, A>::implementation_t : private boost::noncopyable
00417 {
00418
00419 BOOST_STATIC_ASSERT((sizeof(counter_t) == sizeof(std::size_t)));
00420
00421 BOOST_STATIC_ASSERT((sizeof(counter_t) == sizeof(void*)));
00422
00423 struct header_t
00424 {
00425 counter_t count_m;
00426 allocator_type allocator_m;
00427 };
00428
00429 template <typename U>
00430 explicit implementation_t(const U& x, typename copy_sink<U, T>::type = 0) :
00431 value_m(x)
00432 { }
00433
00434 template <typename U>
00435 explicit implementation_t(U x, typename move_sink<U, T>::type = 0) :
00436 value_m(move(x))
00437 { }
00438
00439 implementation_allocator_type get_allocator() const
00440 { return implementation_allocator_type(header_m.get().allocator_m); }
00441
00442 aligned_storage<header_t> header_m;
00443 value_type value_m;
00444 };
00445
00446 template <typename T, typename A>
00447 void copy_on_write<T, A>::init_default()
00448 {
00449 implementation_allocator_type allocator;
00450
00451 default_s = allocate(allocator);
00452
00453 std::atexit(release_default);
00454 }
00455
00456 template <typename T, typename A>
00457 void copy_on_write<T, A>::release_default()
00458 {
00459 release(default_s);
00460 }
00461
00462 #endif
00463
00464
00465
00466 }
00467
00468
00469
00470 using version_1::copy_on_write;
00471
00472
00473
00474 template <typename T, typename A> struct is_movable<copy_on_write<T, A> > : boost::mpl::true_ { };
00475
00476
00477
00478 }
00479
00480
00481
00482 ADOBE_NAME_TYPE_1("copy_on_write:version_1:adobe", adobe::version_1::copy_on_write<T0, adobe::capture_allocator<T0> >)
00483 ADOBE_NAME_TYPE_2("copy_on_write:version_1:adobe", adobe::version_1::copy_on_write<T0, T1>)
00484
00485
00486
00487 #endif
00488
00489