write_at.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. //
  2. // impl/write_at.hpp
  3. // ~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_ASIO_IMPL_WRITE_AT_HPP
  11. #define BOOST_ASIO_IMPL_WRITE_AT_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/associator.hpp>
  16. #include <boost/asio/buffer.hpp>
  17. #include <boost/asio/detail/array_fwd.hpp>
  18. #include <boost/asio/detail/base_from_cancellation_state.hpp>
  19. #include <boost/asio/detail/base_from_completion_cond.hpp>
  20. #include <boost/asio/detail/bind_handler.hpp>
  21. #include <boost/asio/detail/consuming_buffers.hpp>
  22. #include <boost/asio/detail/dependent_type.hpp>
  23. #include <boost/asio/detail/handler_cont_helpers.hpp>
  24. #include <boost/asio/detail/handler_tracking.hpp>
  25. #include <boost/asio/detail/handler_type_requirements.hpp>
  26. #include <boost/asio/detail/non_const_lvalue.hpp>
  27. #include <boost/asio/detail/throw_error.hpp>
  28. #include <boost/asio/detail/push_options.hpp>
  29. namespace boost {
  30. namespace asio {
  31. namespace detail
  32. {
  33. template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence,
  34. typename ConstBufferIterator, typename CompletionCondition>
  35. std::size_t write_at_buffer_sequence(SyncRandomAccessWriteDevice& d,
  36. uint64_t offset, const ConstBufferSequence& buffers,
  37. const ConstBufferIterator&, CompletionCondition completion_condition,
  38. boost::system::error_code& ec)
  39. {
  40. ec = boost::system::error_code();
  41. boost::asio::detail::consuming_buffers<const_buffer,
  42. ConstBufferSequence, ConstBufferIterator> tmp(buffers);
  43. while (!tmp.empty())
  44. {
  45. if (std::size_t max_size = detail::adapt_completion_condition_result(
  46. completion_condition(ec, tmp.total_consumed())))
  47. {
  48. tmp.consume(d.write_some_at(offset + tmp.total_consumed(),
  49. tmp.prepare(max_size), ec));
  50. }
  51. else
  52. break;
  53. }
  54. return tmp.total_consumed();
  55. }
  56. } // namespace detail
  57. template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence,
  58. typename CompletionCondition>
  59. std::size_t write_at(SyncRandomAccessWriteDevice& d,
  60. uint64_t offset, const ConstBufferSequence& buffers,
  61. CompletionCondition completion_condition, boost::system::error_code& ec)
  62. {
  63. return detail::write_at_buffer_sequence(d, offset, buffers,
  64. boost::asio::buffer_sequence_begin(buffers),
  65. static_cast<CompletionCondition&&>(completion_condition), ec);
  66. }
  67. template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence>
  68. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  69. uint64_t offset, const ConstBufferSequence& buffers)
  70. {
  71. boost::system::error_code ec;
  72. std::size_t bytes_transferred = write_at(
  73. d, offset, buffers, transfer_all(), ec);
  74. boost::asio::detail::throw_error(ec, "write_at");
  75. return bytes_transferred;
  76. }
  77. template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence>
  78. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  79. uint64_t offset, const ConstBufferSequence& buffers,
  80. boost::system::error_code& ec)
  81. {
  82. return write_at(d, offset, buffers, transfer_all(), ec);
  83. }
  84. template <typename SyncRandomAccessWriteDevice, typename ConstBufferSequence,
  85. typename CompletionCondition>
  86. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  87. uint64_t offset, const ConstBufferSequence& buffers,
  88. CompletionCondition completion_condition)
  89. {
  90. boost::system::error_code ec;
  91. std::size_t bytes_transferred = write_at(d, offset, buffers,
  92. static_cast<CompletionCondition&&>(completion_condition), ec);
  93. boost::asio::detail::throw_error(ec, "write_at");
  94. return bytes_transferred;
  95. }
  96. #if !defined(BOOST_ASIO_NO_EXTENSIONS)
  97. #if !defined(BOOST_ASIO_NO_IOSTREAM)
  98. template <typename SyncRandomAccessWriteDevice, typename Allocator,
  99. typename CompletionCondition>
  100. std::size_t write_at(SyncRandomAccessWriteDevice& d,
  101. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b,
  102. CompletionCondition completion_condition, boost::system::error_code& ec)
  103. {
  104. std::size_t bytes_transferred = write_at(d, offset, b.data(),
  105. static_cast<CompletionCondition&&>(completion_condition), ec);
  106. b.consume(bytes_transferred);
  107. return bytes_transferred;
  108. }
  109. template <typename SyncRandomAccessWriteDevice, typename Allocator>
  110. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  111. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b)
  112. {
  113. boost::system::error_code ec;
  114. std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec);
  115. boost::asio::detail::throw_error(ec, "write_at");
  116. return bytes_transferred;
  117. }
  118. template <typename SyncRandomAccessWriteDevice, typename Allocator>
  119. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  120. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b,
  121. boost::system::error_code& ec)
  122. {
  123. return write_at(d, offset, b, transfer_all(), ec);
  124. }
  125. template <typename SyncRandomAccessWriteDevice, typename Allocator,
  126. typename CompletionCondition>
  127. inline std::size_t write_at(SyncRandomAccessWriteDevice& d,
  128. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b,
  129. CompletionCondition completion_condition)
  130. {
  131. boost::system::error_code ec;
  132. std::size_t bytes_transferred = write_at(d, offset, b,
  133. static_cast<CompletionCondition&&>(completion_condition), ec);
  134. boost::asio::detail::throw_error(ec, "write_at");
  135. return bytes_transferred;
  136. }
  137. #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
  138. #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
  139. namespace detail
  140. {
  141. template <typename AsyncRandomAccessWriteDevice,
  142. typename ConstBufferSequence, typename ConstBufferIterator,
  143. typename CompletionCondition, typename WriteHandler>
  144. class write_at_op
  145. : public base_from_cancellation_state<WriteHandler>,
  146. base_from_completion_cond<CompletionCondition>
  147. {
  148. public:
  149. write_at_op(AsyncRandomAccessWriteDevice& device,
  150. uint64_t offset, const ConstBufferSequence& buffers,
  151. CompletionCondition& completion_condition, WriteHandler& handler)
  152. : base_from_cancellation_state<WriteHandler>(
  153. handler, enable_partial_cancellation()),
  154. base_from_completion_cond<CompletionCondition>(completion_condition),
  155. device_(device),
  156. offset_(offset),
  157. buffers_(buffers),
  158. start_(0),
  159. handler_(static_cast<WriteHandler&&>(handler))
  160. {
  161. }
  162. write_at_op(const write_at_op& other)
  163. : base_from_cancellation_state<WriteHandler>(other),
  164. base_from_completion_cond<CompletionCondition>(other),
  165. device_(other.device_),
  166. offset_(other.offset_),
  167. buffers_(other.buffers_),
  168. start_(other.start_),
  169. handler_(other.handler_)
  170. {
  171. }
  172. write_at_op(write_at_op&& other)
  173. : base_from_cancellation_state<WriteHandler>(
  174. static_cast<base_from_cancellation_state<WriteHandler>&&>(other)),
  175. base_from_completion_cond<CompletionCondition>(
  176. static_cast<base_from_completion_cond<CompletionCondition>&&>(other)),
  177. device_(other.device_),
  178. offset_(other.offset_),
  179. buffers_(static_cast<buffers_type&&>(other.buffers_)),
  180. start_(other.start_),
  181. handler_(static_cast<WriteHandler&&>(other.handler_))
  182. {
  183. }
  184. void operator()(boost::system::error_code ec,
  185. std::size_t bytes_transferred, int start = 0)
  186. {
  187. std::size_t max_size;
  188. switch (start_ = start)
  189. {
  190. case 1:
  191. max_size = this->check_for_completion(ec, buffers_.total_consumed());
  192. for (;;)
  193. {
  194. {
  195. BOOST_ASIO_HANDLER_LOCATION((__FILE__, __LINE__, "async_write_at"));
  196. device_.async_write_some_at(
  197. offset_ + buffers_.total_consumed(), buffers_.prepare(max_size),
  198. static_cast<write_at_op&&>(*this));
  199. }
  200. return; default:
  201. buffers_.consume(bytes_transferred);
  202. if ((!ec && bytes_transferred == 0) || buffers_.empty())
  203. break;
  204. max_size = this->check_for_completion(ec, buffers_.total_consumed());
  205. if (max_size == 0)
  206. break;
  207. if (this->cancelled() != cancellation_type::none)
  208. {
  209. ec = boost::asio::error::operation_aborted;
  210. break;
  211. }
  212. }
  213. static_cast<WriteHandler&&>(handler_)(
  214. static_cast<const boost::system::error_code&>(ec),
  215. static_cast<const std::size_t&>(buffers_.total_consumed()));
  216. }
  217. }
  218. //private:
  219. typedef boost::asio::detail::consuming_buffers<const_buffer,
  220. ConstBufferSequence, ConstBufferIterator> buffers_type;
  221. AsyncRandomAccessWriteDevice& device_;
  222. uint64_t offset_;
  223. buffers_type buffers_;
  224. int start_;
  225. WriteHandler handler_;
  226. };
  227. template <typename AsyncRandomAccessWriteDevice,
  228. typename ConstBufferSequence, typename ConstBufferIterator,
  229. typename CompletionCondition, typename WriteHandler>
  230. inline bool asio_handler_is_continuation(
  231. write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
  232. ConstBufferIterator, CompletionCondition, WriteHandler>* this_handler)
  233. {
  234. return this_handler->start_ == 0 ? true
  235. : boost_asio_handler_cont_helpers::is_continuation(
  236. this_handler->handler_);
  237. }
  238. template <typename AsyncRandomAccessWriteDevice,
  239. typename ConstBufferSequence, typename ConstBufferIterator,
  240. typename CompletionCondition, typename WriteHandler>
  241. inline void start_write_at_op(AsyncRandomAccessWriteDevice& d,
  242. uint64_t offset, const ConstBufferSequence& buffers,
  243. const ConstBufferIterator&, CompletionCondition& completion_condition,
  244. WriteHandler& handler)
  245. {
  246. detail::write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
  247. ConstBufferIterator, CompletionCondition, WriteHandler>(
  248. d, offset, buffers, completion_condition, handler)(
  249. boost::system::error_code(), 0, 1);
  250. }
  251. template <typename AsyncRandomAccessWriteDevice>
  252. class initiate_async_write_at
  253. {
  254. public:
  255. typedef typename AsyncRandomAccessWriteDevice::executor_type executor_type;
  256. explicit initiate_async_write_at(AsyncRandomAccessWriteDevice& device)
  257. : device_(device)
  258. {
  259. }
  260. executor_type get_executor() const noexcept
  261. {
  262. return device_.get_executor();
  263. }
  264. template <typename WriteHandler, typename ConstBufferSequence,
  265. typename CompletionCondition>
  266. void operator()(WriteHandler&& handler,
  267. uint64_t offset, const ConstBufferSequence& buffers,
  268. CompletionCondition&& completion_cond) const
  269. {
  270. // If you get an error on the following line it means that your handler
  271. // does not meet the documented type requirements for a WriteHandler.
  272. BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
  273. non_const_lvalue<WriteHandler> handler2(handler);
  274. non_const_lvalue<CompletionCondition> completion_cond2(completion_cond);
  275. start_write_at_op(device_, offset, buffers,
  276. boost::asio::buffer_sequence_begin(buffers),
  277. completion_cond2.value, handler2.value);
  278. }
  279. private:
  280. AsyncRandomAccessWriteDevice& device_;
  281. };
  282. } // namespace detail
  283. #if !defined(GENERATING_DOCUMENTATION)
  284. template <template <typename, typename> class Associator,
  285. typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
  286. typename ConstBufferIterator, typename CompletionCondition,
  287. typename WriteHandler, typename DefaultCandidate>
  288. struct associator<Associator,
  289. detail::write_at_op<AsyncRandomAccessWriteDevice, ConstBufferSequence,
  290. ConstBufferIterator, CompletionCondition, WriteHandler>,
  291. DefaultCandidate>
  292. : Associator<WriteHandler, DefaultCandidate>
  293. {
  294. static typename Associator<WriteHandler, DefaultCandidate>::type get(
  295. const detail::write_at_op<AsyncRandomAccessWriteDevice,
  296. ConstBufferSequence, ConstBufferIterator,
  297. CompletionCondition, WriteHandler>& h) noexcept
  298. {
  299. return Associator<WriteHandler, DefaultCandidate>::get(h.handler_);
  300. }
  301. static auto get(
  302. const detail::write_at_op<AsyncRandomAccessWriteDevice,
  303. ConstBufferSequence, ConstBufferIterator,
  304. CompletionCondition, WriteHandler>& h,
  305. const DefaultCandidate& c) noexcept
  306. -> decltype(Associator<WriteHandler, DefaultCandidate>::get(h.handler_, c))
  307. {
  308. return Associator<WriteHandler, DefaultCandidate>::get(h.handler_, c);
  309. }
  310. };
  311. #endif // !defined(GENERATING_DOCUMENTATION)
  312. template <typename AsyncRandomAccessWriteDevice,
  313. typename ConstBufferSequence, typename CompletionCondition,
  314. BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
  315. std::size_t)) WriteToken>
  316. inline auto async_write_at(AsyncRandomAccessWriteDevice& d,
  317. uint64_t offset, const ConstBufferSequence& buffers,
  318. CompletionCondition completion_condition, WriteToken&& token)
  319. -> decltype(
  320. async_initiate<WriteToken,
  321. void (boost::system::error_code, std::size_t)>(
  322. declval<detail::initiate_async_write_at<
  323. AsyncRandomAccessWriteDevice>>(),
  324. token, offset, buffers,
  325. static_cast<CompletionCondition&&>(completion_condition)))
  326. {
  327. return async_initiate<WriteToken,
  328. void (boost::system::error_code, std::size_t)>(
  329. detail::initiate_async_write_at<AsyncRandomAccessWriteDevice>(d),
  330. token, offset, buffers,
  331. static_cast<CompletionCondition&&>(completion_condition));
  332. }
  333. template <typename AsyncRandomAccessWriteDevice, typename ConstBufferSequence,
  334. BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
  335. std::size_t)) WriteToken>
  336. inline auto async_write_at(AsyncRandomAccessWriteDevice& d,
  337. uint64_t offset, const ConstBufferSequence& buffers, WriteToken&& token)
  338. -> decltype(
  339. async_initiate<WriteToken,
  340. void (boost::system::error_code, std::size_t)>(
  341. declval<detail::initiate_async_write_at<
  342. AsyncRandomAccessWriteDevice>>(),
  343. token, offset, buffers, transfer_all()))
  344. {
  345. return async_initiate<WriteToken,
  346. void (boost::system::error_code, std::size_t)>(
  347. detail::initiate_async_write_at<AsyncRandomAccessWriteDevice>(d),
  348. token, offset, buffers, transfer_all());
  349. }
  350. #if !defined(BOOST_ASIO_NO_EXTENSIONS)
  351. #if !defined(BOOST_ASIO_NO_IOSTREAM)
  352. namespace detail
  353. {
  354. template <typename Allocator, typename WriteHandler>
  355. class write_at_streambuf_op
  356. {
  357. public:
  358. write_at_streambuf_op(
  359. boost::asio::basic_streambuf<Allocator>& streambuf,
  360. WriteHandler& handler)
  361. : streambuf_(streambuf),
  362. handler_(static_cast<WriteHandler&&>(handler))
  363. {
  364. }
  365. write_at_streambuf_op(const write_at_streambuf_op& other)
  366. : streambuf_(other.streambuf_),
  367. handler_(other.handler_)
  368. {
  369. }
  370. write_at_streambuf_op(write_at_streambuf_op&& other)
  371. : streambuf_(other.streambuf_),
  372. handler_(static_cast<WriteHandler&&>(other.handler_))
  373. {
  374. }
  375. void operator()(const boost::system::error_code& ec,
  376. const std::size_t bytes_transferred)
  377. {
  378. streambuf_.consume(bytes_transferred);
  379. static_cast<WriteHandler&&>(handler_)(ec, bytes_transferred);
  380. }
  381. //private:
  382. boost::asio::basic_streambuf<Allocator>& streambuf_;
  383. WriteHandler handler_;
  384. };
  385. template <typename Allocator, typename WriteHandler>
  386. inline bool asio_handler_is_continuation(
  387. write_at_streambuf_op<Allocator, WriteHandler>* this_handler)
  388. {
  389. return boost_asio_handler_cont_helpers::is_continuation(
  390. this_handler->handler_);
  391. }
  392. template <typename AsyncRandomAccessWriteDevice>
  393. class initiate_async_write_at_streambuf
  394. {
  395. public:
  396. typedef typename AsyncRandomAccessWriteDevice::executor_type executor_type;
  397. explicit initiate_async_write_at_streambuf(
  398. AsyncRandomAccessWriteDevice& device)
  399. : device_(device)
  400. {
  401. }
  402. executor_type get_executor() const noexcept
  403. {
  404. return device_.get_executor();
  405. }
  406. template <typename WriteHandler,
  407. typename Allocator, typename CompletionCondition>
  408. void operator()(WriteHandler&& handler,
  409. uint64_t offset, basic_streambuf<Allocator>* b,
  410. CompletionCondition&& completion_condition) const
  411. {
  412. // If you get an error on the following line it means that your handler
  413. // does not meet the documented type requirements for a WriteHandler.
  414. BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
  415. non_const_lvalue<WriteHandler> handler2(handler);
  416. async_write_at(device_, offset, b->data(),
  417. static_cast<CompletionCondition&&>(completion_condition),
  418. write_at_streambuf_op<Allocator, decay_t<WriteHandler>>(
  419. *b, handler2.value));
  420. }
  421. private:
  422. AsyncRandomAccessWriteDevice& device_;
  423. };
  424. } // namespace detail
  425. #if !defined(GENERATING_DOCUMENTATION)
  426. template <template <typename, typename> class Associator,
  427. typename Executor, typename WriteHandler, typename DefaultCandidate>
  428. struct associator<Associator,
  429. detail::write_at_streambuf_op<Executor, WriteHandler>,
  430. DefaultCandidate>
  431. : Associator<WriteHandler, DefaultCandidate>
  432. {
  433. static typename Associator<WriteHandler, DefaultCandidate>::type get(
  434. const detail::write_at_streambuf_op<Executor, WriteHandler>& h) noexcept
  435. {
  436. return Associator<WriteHandler, DefaultCandidate>::get(h.handler_);
  437. }
  438. static auto get(
  439. const detail::write_at_streambuf_op<Executor, WriteHandler>& h,
  440. const DefaultCandidate& c) noexcept
  441. -> decltype(Associator<WriteHandler, DefaultCandidate>::get(h.handler_, c))
  442. {
  443. return Associator<WriteHandler, DefaultCandidate>::get(h.handler_, c);
  444. }
  445. };
  446. #endif // !defined(GENERATING_DOCUMENTATION)
  447. template <typename AsyncRandomAccessWriteDevice,
  448. typename Allocator, typename CompletionCondition,
  449. BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
  450. std::size_t)) WriteToken>
  451. inline auto async_write_at(AsyncRandomAccessWriteDevice& d,
  452. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b,
  453. CompletionCondition completion_condition, WriteToken&& token)
  454. -> decltype(
  455. async_initiate<WriteToken,
  456. void (boost::system::error_code, std::size_t)>(
  457. declval<detail::initiate_async_write_at_streambuf<
  458. AsyncRandomAccessWriteDevice>>(),
  459. token, offset, &b,
  460. static_cast<CompletionCondition&&>(completion_condition)))
  461. {
  462. return async_initiate<WriteToken,
  463. void (boost::system::error_code, std::size_t)>(
  464. detail::initiate_async_write_at_streambuf<
  465. AsyncRandomAccessWriteDevice>(d),
  466. token, offset, &b,
  467. static_cast<CompletionCondition&&>(completion_condition));
  468. }
  469. template <typename AsyncRandomAccessWriteDevice, typename Allocator,
  470. BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
  471. std::size_t)) WriteToken>
  472. inline auto async_write_at(AsyncRandomAccessWriteDevice& d,
  473. uint64_t offset, boost::asio::basic_streambuf<Allocator>& b,
  474. WriteToken&& token)
  475. -> decltype(
  476. async_initiate<WriteToken,
  477. void (boost::system::error_code, std::size_t)>(
  478. declval<detail::initiate_async_write_at_streambuf<
  479. AsyncRandomAccessWriteDevice>>(),
  480. token, offset, &b, transfer_all()))
  481. {
  482. return async_initiate<WriteToken,
  483. void (boost::system::error_code, std::size_t)>(
  484. detail::initiate_async_write_at_streambuf<
  485. AsyncRandomAccessWriteDevice>(d),
  486. token, offset, &b, transfer_all());
  487. }
  488. #endif // !defined(BOOST_ASIO_NO_IOSTREAM)
  489. #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
  490. } // namespace asio
  491. } // namespace boost
  492. #include <boost/asio/detail/pop_options.hpp>
  493. #endif // BOOST_ASIO_IMPL_WRITE_AT_HPP