LCOV - code coverage report
Current view: top level - common/stream - pump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 134 145 92.4 %
Date: 2024-12-20 21:24:02 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /* streams working on a lzma/xz-compressed disk file */
      14             : 
      15             : #include "monetdb_config.h"
      16             : #include "stream.h"
      17             : #include "stream_internal.h"
      18             : #include "pump.h"
      19             : 
      20             : #include <assert.h>
      21             : 
      22             : static pump_result pump_in(stream *s);
      23             : static pump_result pump_out(stream *s, pump_action action);
      24             : 
      25             : static ssize_t pump_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt);
      26             : static ssize_t pump_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt);
      27             : static int pump_flush(stream *s, mnstr_flush_level flush_level);
      28             : static void pump_close(stream *s);
      29             : static void pump_destroy(stream *s);
      30             : 
      31             : 
      32             : stream *
      33         185 : pump_stream(stream *inner, pump_state *state)
      34             : {
      35         185 :         assert(inner);
      36         185 :         assert(state);
      37         185 :         assert(state->set_src_win != NULL);
      38         185 :         assert(state->get_dst_win != NULL);
      39         185 :         assert(state->set_dst_win != NULL);
      40         185 :         assert(state->get_buffer != NULL);
      41         185 :         assert(state->worker != NULL);
      42         185 :         assert(state->get_error != NULL);
      43         185 :         assert(state->finalizer != NULL);
      44             : 
      45         185 :         inner_state_t *inner_state = state->inner_state;
      46             : 
      47         185 :         stream *s = create_wrapper_stream(NULL, inner);
      48         185 :         if (s == NULL)
      49             :                 return NULL;
      50             : 
      51         185 :         pump_buffer buf = state->get_buffer(inner_state);
      52         185 :         if (s->readonly) {
      53             :                 // Read from inner stream to src buffer through pumper to outbufs.
      54             :                 // This means the src window starts empty
      55         139 :                 buf.count = 0;
      56         139 :                 state->set_src_win(inner_state, buf);
      57             :         } else {
      58             :                 // from inbufs through pumper to dst buffer to inner stream.
      59             :                 // This means the out window is our whole buffer.
      60             :                 // Check for NULL in case caller has already initialized it
      61             :                 // and written something
      62          46 :                 if (state->get_dst_win(inner_state).start == NULL)
      63           2 :                         state->set_dst_win(inner_state, buf);
      64             :         }
      65             : 
      66         185 :         s->stream_data.p = (void*) state;
      67         185 :         s->read = pump_read;
      68         185 :         s->write = pump_write;
      69         185 :         s->flush = pump_flush;
      70         185 :         s->close = pump_close;
      71         185 :         s->destroy = pump_destroy;
      72         185 :         return s;
      73             : }
      74             : 
      75             : 
      76             : static ssize_t
      77       46873 : pump_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
      78             : {
      79       46873 :         pump_state *state = (pump_state*) s->stream_data.p;
      80       46873 :         inner_state_t *inner_state = state->inner_state;
      81       46873 :         size_t size = elmsize * cnt;
      82             : 
      83       46873 :         state->set_dst_win(inner_state, (pump_buffer){ .start = buf, .count = size});
      84       46873 :         pump_result ret = pump_in(s);
      85       46873 :         if (ret == PUMP_ERROR) {
      86           0 :                 const char *msg = state->get_error(inner_state);
      87           0 :                 if (msg != NULL)
      88           0 :                         msg = "processing failed without further error indication";
      89           0 :                 mnstr_set_error(s, MNSTR_READ_ERROR, "%s", msg);
      90           0 :                 return -1;
      91             :         }
      92             : 
      93       46873 :         char *free_space = state->get_dst_win(inner_state).start;
      94       46873 :         ssize_t nread = free_space - (char*) buf;
      95             : 
      96       46873 :         return nread / (ssize_t) elmsize;
      97             : }
      98             : 
      99             : 
     100             : static ssize_t
     101     1218766 : pump_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
     102             : {
     103     1218766 :         pump_state *state = (pump_state*) s->stream_data.p;
     104     1218766 :         inner_state_t *inner_state = state->inner_state;
     105     1218766 :         size_t size = elmsize * cnt;
     106             : 
     107     1218766 :         if (size == 0)
     108          76 :                 return cnt;
     109             : 
     110     1218690 :         state->set_src_win(inner_state, (pump_buffer){ .start = (void*)buf, .count = size });
     111     1218690 :         pump_result ret = pump_out(s, PUMP_NO_FLUSH);
     112     1218690 :         if (ret == PUMP_ERROR) {
     113           0 :                 const char *msg = state->get_error(inner_state);
     114           0 :                 if (msg != NULL)
     115           0 :                         msg = "processing failed without further error indication";
     116           0 :                 mnstr_set_error(s, MNSTR_READ_ERROR, "%s", msg);
     117           0 :                 return -1;
     118             :         }
     119     1218690 :         ssize_t nwritten = state->get_src_win(inner_state).start - (char*)buf;
     120     1218690 :         return nwritten / (ssize_t) elmsize;
     121             : }
     122             : 
     123             : 
     124         942 : static int pump_flush(stream *s, mnstr_flush_level flush_level)
     125             : {
     126         942 :         pump_state *state = (pump_state*) s->stream_data.p;
     127         942 :         inner_state_t *inner_state = state->inner_state;
     128         942 :         pump_action action;
     129             : 
     130         942 :         switch (flush_level) {
     131             :                 case MNSTR_FLUSH_DATA:
     132             :                         action = PUMP_FLUSH_DATA;
     133             :                         break;
     134         840 :                 case MNSTR_FLUSH_ALL:
     135         840 :                         action = PUMP_FLUSH_ALL;
     136         840 :                         break;
     137             :                 default:
     138           0 :                         assert(0 /* unknown flush_level */);
     139             :                         action = PUMP_FLUSH_DATA;
     140             :                         break;
     141             :         }
     142             : 
     143         942 :         state->set_src_win(inner_state, (pump_buffer){ .start = NULL, .count = 0 });
     144         942 :         ssize_t nwritten = pump_out(s, action);
     145         942 :         if (nwritten < 0)
     146             :                 return -1;
     147             :         else
     148         942 :                 return mnstr_flush(s->inner, flush_level);
     149             : }
     150             : 
     151             : 
     152             : static void
     153         171 : pump_close(stream *s)
     154             : {
     155         171 :         pump_state *state = (pump_state*) s->stream_data.p;
     156         171 :         inner_state_t *inner_state = state->inner_state;
     157             : 
     158         171 :         if (!s->readonly) {
     159          32 :                 state->set_src_win(inner_state, (pump_buffer){ .start = NULL, .count = 0 });
     160          32 :                 pump_out(s, PUMP_FINISH);
     161             :         }
     162         171 :         mnstr_close(s->inner);
     163         171 : }
     164             : 
     165             : 
     166             : static void
     167         171 : pump_destroy(stream *s)
     168             : {
     169         171 :         pump_state *state = (pump_state*) s->stream_data.p;
     170         171 :         inner_state_t *inner_state = state->inner_state;
     171             : 
     172         171 :         state->finalizer(inner_state);
     173         171 :         free(state);
     174         171 :         mnstr_destroy(s->inner);
     175         171 :         destroy_stream(s);
     176         171 : }
     177             : 
     178             : static pump_result
     179       46873 : pump_in(stream *s)
     180             : {
     181       46873 :         pump_state *state = (pump_state *) s->stream_data.p;
     182       46873 :         inner_state_t *inner_state = state->inner_state;
     183             : 
     184       46873 :         char *before = state->get_dst_win(inner_state).start;
     185       46873 :         (void) before; // nice while in the debugger
     186             : 
     187       46873 :         pump_buffer buffer = state->get_buffer(inner_state);
     188       81643 :         while (1) {
     189      128516 :                 pump_buffer dst = state->get_dst_win(inner_state);
     190      128516 :                 pump_buffer src = state->get_src_win(inner_state);
     191             : 
     192      128516 :                 if (dst.count == 0)
     193             :                         // Output buffer is full, we're done.
     194       46873 :                         return PUMP_OK;
     195             : 
     196             :                 // Handle input, if possible and necessary
     197       81841 :                 if (src.start != NULL && src.count == 0) {
     198             :                         // start != NULL means we haven't encountered EOF yet
     199             : 
     200       34980 :                         ssize_t nread = mnstr_read(s->inner, buffer.start, 1, buffer.count);
     201             : 
     202       34980 :                         if (nread < 0)
     203             :                                 // Error. Return directly, discarding any data lingering
     204             :                                 // in the internal state.
     205             :                                 return PUMP_ERROR;
     206       34980 :                         if (nread == 0) {
     207             :                                 // Set to NULL so we'll remember next time.
     208             :                                 // Maybe there is some data in the internal state we don't
     209             :                                 // return immediately.
     210         131 :                                 src = (pump_buffer){.start=NULL, .count=0};
     211         131 :                                 s->eof |= s->inner->eof;
     212             :                         } else
     213             :                                 // All good
     214       34849 :                                 src = (pump_buffer) { .start = buffer.start, .count = nread};
     215             : 
     216       34980 :                         state->set_src_win(inner_state, src);
     217             :                 }
     218             : 
     219       81841 :                 pump_action action = (src.start != NULL) ? PUMP_NO_FLUSH : PUMP_FINISH;
     220             : 
     221             :                 // Try to make some progress
     222       81841 :                 assert(dst.count > 0);
     223       81841 :                 assert(src.count > 0 || action == PUMP_FINISH);
     224       81841 :                 pump_result ret = state->worker(inner_state, action);
     225       81841 :                 if (ret == PUMP_ERROR)
     226             :                         return PUMP_ERROR;
     227             : 
     228       81841 :                 if (ret == PUMP_END)
     229             :                         // If you say so
     230             :                         return PUMP_END;
     231             : 
     232             :                 // If we get here we made some progress so we're ready for a new iteration.
     233             :         }
     234             : }
     235             : 
     236             : 
     237             : static pump_result
     238     1219664 : pump_out(stream *s, pump_action action)
     239             : {
     240     1219664 :         pump_state *state = (pump_state *) s->stream_data.p;
     241     1219664 :         inner_state_t *inner_state = state->inner_state;
     242             : 
     243     1219664 :         void *before = state->get_src_win(inner_state).start;
     244     1219664 :         (void) before; // nice while in the debugger
     245             : 
     246     1219664 :         pump_buffer buffer = state->get_buffer(inner_state);
     247             : 
     248     1227584 :         while (1) {
     249     1223624 :                 pump_buffer dst = state->get_dst_win(inner_state);
     250     1223624 :                 pump_buffer src = state->get_src_win(inner_state);
     251             : 
     252             :                 // Make sure there is room in the output buffer
     253     1223624 :                 assert(state->elbow_room <= buffer.count);
     254     1223624 :                 if (dst.count == 0 || dst.count < state->elbow_room) {
     255         319 :                         size_t amount = dst.start - buffer.start;
     256         319 :                         ssize_t nwritten = mnstr_write(s->inner, buffer.start, 1, amount);
     257         319 :                         if (nwritten != (ssize_t)amount)
     258     1219664 :                                 return PUMP_ERROR;
     259         319 :                         dst = buffer;
     260         319 :                         state->set_dst_win(inner_state, dst); // reset output window
     261             :                 }
     262             : 
     263             :                 // Try to make progress
     264     1223624 :                 pump_result ret = state->worker(inner_state, action);
     265     1223624 :                 if (ret == PUMP_ERROR)
     266             :                         return PUMP_ERROR;
     267             : 
     268             :                 // src and dst have been invalidated by the call to worker
     269     1223411 :                 dst = state->get_dst_win(inner_state);
     270     1223411 :                 src = state->get_src_win(inner_state);
     271             : 
     272             :                 // There was no error but if input is still available, we definitely
     273             :                 // need another round
     274     1223411 :                 if (src.count > 0)
     275        3746 :                         continue;
     276             : 
     277             :                 // Though the input data has been consumed, some of it might still
     278             :                 // linger in the internal state.
     279     1219665 :                 if (action == PUMP_NO_FLUSH) {
     280             :                         // Let it linger, we'll combine it with the next batch
     281     1218690 :                         assert(ret == PUMP_OK); // worker would never PUMP_END, would it?
     282             :                         return PUMP_OK;
     283             :                 }
     284             : 
     285             :                 // We are flushing or finishing or whatever.
     286             :                 // We may need to do more iterations to fully flush the internal state.
     287             :                 // Is there any internal state left?
     288         975 :                 if (ret == PUMP_OK)
     289             :                         // yes, there is
     290         214 :                         continue;
     291             : 
     292             :                 // All internal state has been drained.
     293             :                 // Now drain the output buffer
     294         761 :                 assert(ret == PUMP_END);
     295         761 :                 size_t amount = dst.start - buffer.start;
     296         761 :                 if (amount > 0) {
     297         756 :                         ssize_t nwritten = mnstr_write(s->inner, buffer.start, 1, amount);
     298         756 :                         if (nwritten != (ssize_t)amount)
     299             :                                 return PUMP_ERROR;
     300             :                 }
     301         761 :                 state->set_dst_win(inner_state, buffer); // reset output window
     302         761 :                 return PUMP_END;
     303             :         }
     304             : 
     305             : 
     306             : }

Generated by: LCOV version 1.14