summaryrefslogtreecommitdiff
path: root/src/lib/ecore/efl_io_copier.eo
blob: 8b7301b14ba525a09f005ea82cc4f1d865e3778c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
import eina_types;

class @beta Efl.Io.Copier extends Efl.Loop_Consumer implements Efl.Io.Closer {
    [[Copy from an @Efl.Io.Reader source to @Efl.Io.Writer destination.

      During usage it will keep reference to @.source and
      @.destination objects, automatically releasing them on
      destruction.

      By default the read-write process is done based on fixed-size
      chunks (@.read_chunk_size) but if @.line_delimiter is set,
      the behavior changes to wait for said delimiter or until a maximum
      buffer limit is reached (@.buffer_limit).

      While a @.source is mandatory for the copier to operate
      properly the @.destination is optional: if it's not provided,
      all read data is stored in an internal buffer that can be
      retrieved using @.binbuf_steal, usually called when one of
      "data", "line" or "done" events are emitted.

      Most important events:

        - The "data" event is general and notifies some data was
          written to @.destination (if any, otherwise it will be
          reported as data when it's read from @.source).

        - The "line" event is only emitted when @.line_delimiter is
          set and not empty. It's similar to "data".

        - The "done" event is emitted if @.source is
          @Efl.Io.Reader.eos $true and all data was written to
          @.destination (if any, otherwise it will be reported when
          all data is read from @.source).

        - The "error" event is reported if the @Efl.Io.Reader.read,
          @Efl.Io.Writer.write or some other internal error occurs,
          such as being out of memory. Another common error is ETIMEDOUT if
          @.timeout_inactivity is set.

      A copier is handy for simplifying common I/O use cases, such as:

        - Reading a file or download content to memory: provide only the
          source and wait for "done" event, calling @.binbuf_steal
          afterwards.

        - Downloading content to disk: provide a network socket as source
          and use @Efl.Io.File as destination, then wait for "done"
          event.

        - Linking two I/O streams: provide both source and destination
          streams, such as @Efl.Io.Stdin and @Efl.Io.Stdout, or some
          network socket. As data is received from source it will be
          copied to destination in an endless (asynchronous) loop. You
          may monitor for "done" if the source is closed.

      If @Efl.Io.Closer.close is called, then it will be called on
      @.source and @.destination if they implement those interfaces.

      @Efl.Io.Closer.close_on_exec and
      @Efl.Io.Closer.close_on_invalidate are respected and applied to
      both source and destination. Both default to $true.
    ]]

    methods {
        @property source {
           [[Copier source object.

             The source object must implement the @Efl.Io.Reader
             interface and will provide data to the copier, thus it
             must be provided in order for the copier to work.

             The copier will monitor @Efl.Io.Reader.can_read property
             and "can_read,changed" event. When $true a
             chunk (limited by @.read_chunk_size) is read using
             @Efl.Io.Reader.read into an intermediate storage buffer
             which can grow up to the @.buffer_limit.

             If there's a @.destination, the read data is written
             there and afterwards "data" and "line" events are
             dispatched. If no destination, it's accumulated in the
             internal storage and "data" and "line" events are
             dispatched immediately.

             If there is a @.line_delimiter set, then copier will use
             that in order to attempt to write a full line at a time,
             including the delimiter. Exceptions may be if the
             @.source object emitted "eos" and there is no trailing
             delimiter (i.e: missing trailing newline in files) or if the
             @.buffer_limit is reached.

             Once @Efl.Io.Reader.eos is $true or "eos" event happen,
             then the read process is over. If there is no
             @.destination set, then the process is over and "done"
             event is called. If there's a @.destination, then all
             data must be flushed to it, draining the internal
             intermediate storage before "done" event is emitted.

             If the source object implements @Efl.Io.Sizer interface,
             then the @.progress will report the total size. If the
             destination object also implements @Efl.Io.Sizer then it
             will be resized to match the source size/ It also offers
             hints to enhance performance such as pre-allocating
             the total amount and avoid dynamic resize.

             If the source object implements @Efl.Io.Closer and it's
             not closed, it will be closed when the copier itself
             is. This may happen, for example, when the copier is
             deleted and @Efl.Io.Closer.close_on_invalidate is $true
             (the default).

             Common source classes are @Efl.Io.Buffer (if fixed data
             exists in memory), @Efl.Io.Queue (used to stream
             in-memory data) and @Efl.Io.File (data is present in the
             file system). Networking classes are also common source
             objects.
           ]]
           get {
           }
           set {
                [[Constructor-only property to set where to read data from]]
           }
           values {
                source: Efl.Io.Reader; [[@Efl.Io.Reader source]]
           }
        }

        @property destination {
           [[Copier destination object.

             If set it must implement @Efl.Io.Writer interface and
             will receive read data once @Efl.Io.Writer.can_write
             reports $true, this is monitored using the
             "can_write,changed" event.

             The copier will attempt to write all internal
             intermediate storage data at once, however the
             destination object may consume less. The actual data
             written is emitted in the "data" and "line" events.

             If there is an @.line_delimiter set then copier will use
             it in order to attempt to write one full line at a time,
             including the delimiter. Exceptions may be if the
             @.source object emitted "eos" and there is no trailing
             delimiter (ie: missing trailing newline in files), or the
             @.buffer_limit is reached.

             If @.source is flagged "eos" and all data is written to the
             destination then the "done" event is emitted.

             If the destination is not set (ie: NULL) then data is kept
             in a internal @Eina.Binbuf, which can be stolen with
             @.binbuf_steal once "data" or "line" events are
             emitted. It exists as a useful shortcut to easily drain
             readers and store all data in memory with no need to use an
             @Efl.Io.Buffer or @Efl.Io.Copier  -- a source
             and copier are enough.

             If both source and destination object implements
             @Efl.Io.Sizer, then the destination will be resized to match
             the source size. It also offers hints to enhance
             performance such as pre-allocating the total amount and
             avoid dynamic resize.

             If the destination object implements @Efl.Io.Closer and it's
             not closed, it will be closed when the copier itself
             is. This may happen, for example, when the copier is
             deleted and @Efl.Io.Closer.close_on_invalidate is $true
             (the default).

             Common destination classes are @Efl.Io.Buffer (better to
             wait for all data in memory), @Efl.Io.Queue (to handle
             streaming protocols) and @Efl.Io.File (stores data to
             disk). Networking classes are also common destination
             objects.
           ]]
           get {
           }
           set {
                [[Constructor-only property to set where to write data to]]
           }
           values {
                destination: Efl.Io.Writer; [[@Efl.Io.Writer destination]]
           }
        }

        @property line_delimiter {
            [[If there is a line delimiter, reads will buffer/queue up to the line delimiter before calling
              @Efl.Io.Writer.write on the @.destination and the event line is emitted with the current line.
              The line may include the delimiter unless it's end-of-stream on @.source or @.buffer_limit was reached.]]
            get { }
            set {
               [[Changes line delimiter to use. If empty, no delimiter is to be used]]
            }
            values {
                slice: const(Eina.Slice); [[The contents may contain \0 and will be copied]]
            }
        }

        @property buffer_limit {
           [[Copier buffer limit property, in bytes.

             During the read-write cycle, an intermediate storage
             buffer is used. By default it's zero -- unlimited and
             will grow as needed if @.source provides data and
             @.destination do not consume it (or if there is no
             @.destination).

             However when reading data from untrusted sources like
             network system memory can be exhausted by flooding
             the copier. In these cases using a buffer limit is
             recommended.

             When the buffer limit is reached the copier will pause
             reading data from @.source until @.destination consumes
             it. If there is no @.destination set, user should call
             @.binbuf_steal to consume data and reset buffer usage.

             Setting a buffer limit smaller than current
             @.read_chunk_size will automatically change
             @.read_chunk_size to the new buffer limit.
           ]]
           get {
           }
           set {
                [[Constructor-only property to set buffer limit. 0 is unlimited]]
           }
           values {
                size: size; [[Defines a maximum buffer limit, or 0 to allow unlimited amount of bytes]]
           }
        }

        @property read_chunk_size {
           [[Copier read chunk size property, in bytes.

             When a @.source is flagged with @Efl.Io.Reader.can_read
             $true, data will be read using @Efl.Io.Reader.read into
             an intermediate buffer of this size.

             Setting this value large enough may reduce number of
             @Efl.Io.Reader.read, improving performance at the expense
             of more memory consumption.

             This value is bounded by @.buffer_limit if it's set.

             By default it's 4096.
           ]]
           get {
           }
           set {
                [[Set chunk size for each basic @Efl.Io.Reader.read operation.]]
           }
           values {
                size: size; [[This is the chunk size to use for read operations]]
           }
        }

        @property progress {
            [[Progress for read and write.

              Reports value read from @.source, written to
              @.destination and the total, if the source implements
              @Efl.Io.Sizer.
            ]]
            get {
            }
            values {
                read: uint64 @optional; [[Amount of bytes read from source]]
                written: uint64 @optional; [[Amount of bytes written to destination]]
                total: uint64 @optional; [[If @.source is an @Efl.Io.Sizer, its total size. Otherwise 0 to
                                           report unknown size]]
           }
        }

        @property timeout_inactivity {
            [[Terminates the copier with ETIMEDOUT if it becomes inactive for some time.

              If the copier cannot perform any reads or writes in the given
              amount of seconds then the copier will emit "error"
              event with ETIMEDOUT value.

              This is specified in seconds and is only active for
              greater-than zero. Defaults to inactive.
            ]]
            values {
                seconds: double; [[Number inactive seconds to timeout this copier. If zero or less, it will be
                                   disabled.]]
            }
        }

        @property done {
            [[Reports if copier is done.

              A copier is done if source reached "eos" and all data
              is written to "destination".

              The copier is also done when it's @Efl.Io.Closer.closed.
            ]]
            get { }
            set @protected { }
            values {
                done: bool; [[If $true, source is "eos" and all data was written to "destination". If $false, it's
                              still pending some more copies]]
            }
        }

        binbuf_steal {
           [[Steals the internal binbuf and returns it to caller.

             The buffer is then owned by caller which should call
             eina_binbuf_free() when it's done.

             Usually call this method when no @.destination is set, in
             which case you should wait for "done", "data" or "line"
             events and then call it to retrieve and own the data.
           ]]
           return: ptr(Eina.Binbuf) @owned @no_unused; [[Binbuf]]
        }

        @property pending_size {
            [[Returns the size of the pending buffer.

              The pending buffer is an intermediate buffer where data
              is read from @.source before it's written to
              @.destination, if any.

              This is the equivalent of the size of returned by
              @.binbuf_steal however it doesn't steal or modify the
              buffer at all, just queries its internal size.
            ]]
            get { }
            values {
                size: size; [[The pending buffer size, in bytes.]]
            }
        }

        flush {
            [[Forces reading from source and writing to destination.

              This executes a single read->write cycle if more data
              can be read from source (ie: not EOS) or not all data
              is written to destination, then $false is
              returned. To forcefully drain source and write all
              contents to destination, use in a loop until it returns
              $true.

              The return value matches the "done" event: that is, when
              $true is returned, the "done" event is emitted.

              This function may also emit "progress" and "error"
              events.

              Note: this function may block the main loop execution
              until operations are complete! This is bad for usability as
              user interface or other operations may freeze. A better
              approach is to operate asynchronously and wait for the
              "done" event.
            ]]
            params {
                may_block: bool; [[If $true, then @Efl.Io.Reader.can_read and @Efl.Io.Writer.can_write are not checked
                                   and the call may block.]]
                ignore_line_delimiter: bool; [[Forces flush ignoring line delimiters]]
            }
            return: bool(true); [[$true on success, $false otherwise]]
        }
    }

    events {
        done: void; [[All available data was copied from source to destination]]
        error: Eina.Error; [[An error happened and the copy stopped]]
        progress: void; [[Total size changed or Data was read/written]]
        data: ptr(const(Eina.Slice)); [[When data is read to internal buffer, it's emitted in this event. The memory
                                        is only valid during event callback dispatched and should not be modified.]]
        line: ptr(const(Eina.Slice)); [[If @.line_delimiter is set, will be emitted with current line. The memory is
                                        only valid during event callback dispatched and should not be modified.]]
    }

    implements {
       Efl.Object.constructor;
       Efl.Object.finalize;
       Efl.Object.invalidate;
       Efl.Object.destructor;
       Efl.Io.Closer.close;
       Efl.Io.Closer.closed { get; }
       Efl.Io.Closer.close_on_exec { get; set; }
       Efl.Io.Closer.close_on_invalidate { get; set; }
    }
}