buf.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /** @file buf.c
  2. * @brief Dynamic buffer.
  3. *
  4. */
  5. #include <stdlib.h>
  6. #include <stdint.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <assert.h>
  10. #include "faux/faux.h"
  11. #include "faux/str.h"
  12. #include "faux/buf.h"
  13. #define DATA_CHUNK 4096
  14. struct faux_buf_s {
  15. size_t limit;
  16. faux_list_t *list;
  17. size_t rpos;
  18. size_t wpos;
  19. size_t size;
  20. };
  21. /** @brief Create new buf I/O object.
  22. *
  23. * Constructor gets associated file descriptor to operate on it. File
  24. * descriptor must be nonblocked. If not so then constructor will set
  25. * nonblock flag itself.
  26. *
  27. * @param [in] fd File descriptor.
  28. * @return Allocated object or NULL on error.
  29. */
  30. faux_buf_t *faux_buf_new(void)
  31. {
  32. faux_buf_t *buf = NULL;
  33. buf = faux_zmalloc(sizeof(*buf));
  34. assert(buf);
  35. if (!buf)
  36. return NULL;
  37. // Init
  38. buf->limit = FAUX_BUF_UNLIMITED;
  39. buf->list = faux_list_new(FAUX_LIST_UNSORTED, FAUX_LIST_NONUNIQUE,
  40. NULL, NULL, faux_free);
  41. buf->rpos = 0;
  42. buf->wpos = 0;
  43. buf->size = 0;
  44. return buf;
  45. }
  46. /** @brief Free buf I/O object.
  47. *
  48. * @param [in] buf I/O object.
  49. */
  50. void faux_buf_free(faux_buf_t *buf)
  51. {
  52. if (!buf)
  53. return;
  54. faux_list_free(buf->list);
  55. faux_free(buf);
  56. }
  57. ssize_t faux_buf_limit(const faux_buf_t *buf)
  58. {
  59. assert(buf);
  60. if (!buf)
  61. return -1;
  62. return buf->limit;
  63. }
  64. /** @brief Set size limit.
  65. *
  66. * Read limits define conditions when the read callback will be executed.
  67. * Buffer must contain data amount greater or equal to "min" value. Callback
  68. * will not get data amount greater than "max" value. If min == max then
  69. * callback will be executed with fixed data size. The "max" value can be "0".
  70. * It means indefinite i.e. data transferred to callback can be really large.
  71. *
  72. * @param [in] buf Allocated and initialized buf I/O object.
  73. * @param [in] min Minimal data amount.
  74. * @param [in] max Maximal data amount. The "0" means indefinite.
  75. * @return BOOL_TRUE - success, BOOL_FALSE - error.
  76. */
  77. bool_t faux_buf_set_limit(faux_buf_t *buf, size_t limit)
  78. {
  79. assert(buf);
  80. if (!buf)
  81. return BOOL_FALSE;
  82. buf->limit = limit;
  83. return BOOL_TRUE;
  84. }
  85. #if 0
  86. /** @brief Get amount of unused space within current data chunk.
  87. *
  88. * Inernal static function.
  89. *
  90. * @param [in] list Internal buffer (list of chunks) to inspect.
  91. * @param [in] pos Current write position within last chunk
  92. * @return Size of unused space or < 0 on error.
  93. */
  94. static ssize_t free_space(faux_list_t *list, size_t pos)
  95. {
  96. if (!list)
  97. return -1;
  98. if (faux_list_len(list) == 0)
  99. return 0;
  100. return (DATA_CHUNK - pos);
  101. }
  102. /** @brief buf data write.
  103. *
  104. * All given data will be stored to internal buffer (list of data chunks).
  105. * Then function will try to write stored data to file descriptor in
  106. * non-blocking mode. Note some data can be left within buffer. In this case
  107. * the "stall" callback will be executed to inform about it. To try to write
  108. * the rest of the data user can be call faux_buf_out() function. Both
  109. * functions will not block.
  110. *
  111. * @param [in] buf Allocated and initialized buf I/O object.
  112. * @param [in] data Data buffer to write.
  113. * @param [in] len Data length to write.
  114. * @return Length of stored/writed data or < 0 on error.
  115. */
  116. ssize_t faux_buf_write(faux_buf_t *buf, void *data, size_t len)
  117. {
  118. void *new_chunk = NULL;
  119. size_t data_left = len;
  120. assert(buf);
  121. if (!buf)
  122. return -1;
  123. assert(data);
  124. if (!data)
  125. return -1;
  126. while (data_left != 0) {
  127. ssize_t bytes_free = 0;
  128. size_t copy_len = 0;
  129. char *chunk_ptr = NULL;
  130. // Allocate new chunk if necessary
  131. bytes_free = free_space(buf->o_list, buf->o_wpos);
  132. if (bytes_free < 0)
  133. return -1;
  134. if (0 == bytes_free) {
  135. new_chunk = faux_malloc(DATA_CHUNK);
  136. assert(new_chunk);
  137. faux_list_add(buf->o_list, new_chunk);
  138. buf->o_wpos = 0;
  139. bytes_free = free_space(buf->o_list, buf->o_wpos);
  140. }
  141. // Copy data
  142. chunk_ptr = faux_list_data(faux_list_tail(buf->o_list));
  143. copy_len = (data_left < (size_t)bytes_free) ? data_left : (size_t)bytes_free;
  144. memcpy(chunk_ptr + buf->o_wpos, data + len - data_left,
  145. copy_len);
  146. buf->o_wpos += copy_len;
  147. data_left -= copy_len;
  148. buf->o_size += copy_len;
  149. if (buf->o_size >= buf->o_overflow)
  150. return -1;
  151. }
  152. // Try to real write data to fd in nonblocked mode
  153. faux_buf_out(buf);
  154. return len;
  155. }
  156. /** @brief Get amount of available data within first chunk.
  157. *
  158. * Inernal static function.
  159. *
  160. * @param [in] list Internal buffer (list of chunks) to inspect.
  161. * @param [in] rpos Current read position within chunk.
  162. * @param [in] wpos Current write position within chunk.
  163. * @return Available data length or < 0 on error.
  164. */
  165. static ssize_t data_avail(faux_list_t *list, size_t rpos, size_t wpos)
  166. {
  167. size_t len = 0;
  168. if (!list)
  169. return -1;
  170. len = faux_list_len(list);
  171. if (len == 0)
  172. return 0;
  173. if (len > 1)
  174. return (DATA_CHUNK - rpos);
  175. // Single chunk
  176. return (wpos - rpos);
  177. }
  178. /** @brief Write output buffer to fd in non-blocking mode.
  179. *
  180. * Previously data must be written to internal buffer by faux_buf_write()
  181. * function. But some data can be left within internal buffer because can't be
  182. * written to fd in non-blocking mode. This function tries to write the rest of
  183. * data to fd in non-blocking mode. So function doesn't block. It can be called
  184. * after select() or poll() if fd is ready to be written to. If function can't
  185. * to write all buffer to fd it executes "stall" callback to inform about it.
  186. *
  187. * @param [in] buf Allocated and initialized buf I/O object.
  188. * @return Length of data actually written or < 0 on error.
  189. */
  190. ssize_t faux_buf_out(faux_buf_t *buf)
  191. {
  192. ssize_t total_written = 0;
  193. assert(buf);
  194. if (!buf)
  195. return -1;
  196. while (buf->o_size > 0) {
  197. faux_list_node_t *node = NULL;
  198. char *chunk_ptr = NULL;
  199. ssize_t data_to_write = 0;
  200. ssize_t bytes_written = 0;
  201. bool_t postpone = BOOL_FALSE;
  202. node = faux_list_head(buf->o_list);
  203. if (!node) // List is empty while o_size > 0
  204. return -1;
  205. chunk_ptr = faux_list_data(node);
  206. data_to_write = data_avail(buf->o_list,
  207. buf->o_rpos, buf->o_wpos);
  208. if (data_to_write <= 0) // Strange case
  209. return -1;
  210. bytes_written = write(buf->fd, chunk_ptr + buf->o_rpos,
  211. data_to_write);
  212. if (bytes_written > 0) {
  213. buf->o_size -= bytes_written;
  214. total_written += bytes_written;
  215. }
  216. if (bytes_written < 0) {
  217. if ( // Something went wrong
  218. (errno != EINTR) &&
  219. (errno != EAGAIN) &&
  220. (errno != EWOULDBLOCK)
  221. )
  222. return -1;
  223. // Postpone next read
  224. postpone = BOOL_TRUE;
  225. // Not whole data block was written
  226. } else if (bytes_written != data_to_write) {
  227. buf->o_rpos += bytes_written;
  228. // Postpone next read
  229. postpone = BOOL_TRUE;
  230. }
  231. // Postponed
  232. if (postpone) {
  233. // Execute callback
  234. if (buf->stall_cb)
  235. buf->stall_cb(buf, buf->o_size,
  236. buf->stall_udata);
  237. break;
  238. }
  239. // Not postponed. Current chunk was fully written. So
  240. // remove it from list.
  241. buf->o_rpos = 0;
  242. faux_list_del(buf->o_list, node);
  243. }
  244. return total_written;
  245. }
  246. /** @brief Read data and store it to internal buffer in non-blocking mode.
  247. *
  248. * Reads fd and puts data to internal buffer. It can't be blocked. If length of
  249. * data stored within internal buffer is greater or equal than "min" limit then
  250. * function will execute "read" callback. It allocates linear buffer, copies
  251. * data to it and give it to callback. Note this function will never free
  252. * allocated buffer. So callback must do it or it must be done later. Function
  253. * will not allocate buffer larger than "max" read limit. If "max" limit is "0"
  254. * (it means indefinite) then function will pass all available data to callback.
  255. *
  256. * @param [in] buf Allocated and initialized buf I/O object.
  257. * @return Length of data actually readed or < 0 on error.
  258. */
  259. ssize_t faux_buf_in(faux_buf_t *buf)
  260. {
  261. void *new_chunk = NULL;
  262. ssize_t total_readed = 0;
  263. ssize_t bytes_readed = 0;
  264. ssize_t bytes_free = 0; // Free space within current (last) chunk
  265. assert(buf);
  266. if (!buf)
  267. return -1;
  268. do {
  269. char *chunk_ptr = NULL;
  270. // Allocate new chunk if necessary
  271. bytes_free = free_space(buf->i_list, buf->i_wpos);
  272. if (bytes_free < 0)
  273. return -1;
  274. if (0 == bytes_free) { // We need to allocate additional chunk
  275. new_chunk = faux_malloc(DATA_CHUNK);
  276. assert(new_chunk);
  277. faux_list_add(buf->i_list, new_chunk);
  278. buf->i_wpos = 0;
  279. bytes_free = free_space(buf->i_list, buf->i_wpos);
  280. }
  281. // Read data to last chunk
  282. chunk_ptr = faux_list_data(faux_list_tail(buf->i_list));
  283. bytes_readed = read(buf->fd, chunk_ptr + buf->i_wpos, bytes_free);
  284. if (bytes_readed < 0) {
  285. if ( // Something went wrong
  286. (errno != EINTR) &&
  287. (errno != EAGAIN) &&
  288. (errno != EWOULDBLOCK)
  289. )
  290. return -1;
  291. }
  292. if (bytes_readed > 0) {
  293. buf->i_wpos += bytes_readed;
  294. buf->i_size += bytes_readed;
  295. total_readed += bytes_readed;
  296. }
  297. if (buf->i_size >= buf->i_overflow)
  298. return -1;
  299. // Check for amount of stored data
  300. while (buf->i_size >= buf->min) {
  301. size_t copy_len = 0;
  302. size_t full_size = 0;
  303. char *buf = NULL;
  304. char *buf_ptr = NULL;
  305. if (FAUX_buf_UNLIMITED == buf->max) { // Indefinite
  306. copy_len = buf->i_size; // Take all data
  307. } else {
  308. copy_len = (buf->i_size < buf->max) ?
  309. buf->i_size : buf->max;
  310. }
  311. full_size = copy_len; // Save full length value
  312. buf = faux_malloc(full_size);
  313. buf_ptr = buf;
  314. while (copy_len > 0) {
  315. size_t data_to_write = 0;
  316. faux_list_node_t *node = faux_list_head(buf->i_list);
  317. char *chunk_ptr = NULL;
  318. if (!node) // Something went wrong
  319. return -1;
  320. chunk_ptr = faux_list_data(node);
  321. data_to_write = data_avail(buf->i_list,
  322. buf->i_rpos, buf->i_wpos);
  323. if (copy_len < data_to_write)
  324. data_to_write = copy_len;
  325. memcpy(buf_ptr, chunk_ptr + buf->i_rpos,
  326. data_to_write);
  327. copy_len -= data_to_write;
  328. buf->i_size -= data_to_write;
  329. buf->i_rpos += data_to_write;
  330. buf_ptr += data_to_write;
  331. if (data_avail(buf->i_list,
  332. buf->i_rpos, buf->i_wpos) <= 0) {
  333. buf->i_rpos = 0;
  334. faux_list_del(buf->i_list, node);
  335. }
  336. }
  337. // Execute callback
  338. if (buf->read_cb)
  339. buf->read_cb(buf, buf,
  340. full_size, buf->read_udata);
  341. }
  342. } while (bytes_readed == bytes_free);
  343. return total_readed;
  344. }
  345. #endif