2 * Copyright (C) 2014 Wim Taymans <wtaymans@redhat.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
25 #include <glib/gstdio.h>
27 #include "gstsparsefile.h"
30 #include <io.h> /* lseek, open, close, read */
32 #define lseek _lseeki64
39 #ifdef __BIONIC__ /* Android */
47 #define FSEEK_FILE(file,offset) (fseeko (file, (off_t) offset, SEEK_SET) != 0)
48 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
49 #define FSEEK_FILE(file,offset) (lseek (fileno (file), (off_t) offset, SEEK_SET) == (off_t) -1)
51 #define FSEEK_FILE(file,offset) (fseek (file, offset, SEEK_SET) != 0)
54 #define GST_SPARSE_FILE_IO_ERROR \
55 g_quark_from_static_string("gst-sparse-file-io-error-quark")
57 static GstSparseFileIOErrorEnum
58 gst_sparse_file_io_error_from_errno (gint err_no);
60 typedef struct _GstSparseRange GstSparseRange;
62 struct _GstSparseRange
70 #define RANGE_CONTAINS(r,o) ((r)->start <= (o) && (r)->stop > (o))
78 GstSparseRange *ranges;
81 GstSparseRange *write_range;
82 GstSparseRange *read_range;
85 static GstSparseRange *
86 get_write_range (GstSparseFile * file, gsize offset)
88 GstSparseRange *next, *prev, *result = NULL;
90 if (file->write_range && file->write_range->stop == offset)
91 return file->write_range;
96 if (next->start > offset)
99 if (next->stop >= offset) {
106 if (result == NULL) {
107 result = g_slice_new0 (GstSparseRange);
108 result->start = offset;
109 result->stop = offset;
115 file->ranges = result;
117 file->write_range = result;
118 file->read_range = NULL;
125 static GstSparseRange *
126 get_read_range (GstSparseFile * file, gsize offset, gsize count)
128 GstSparseRange *walk, *result = NULL;
130 if (file->read_range && RANGE_CONTAINS (file->read_range, offset))
131 return file->read_range;
133 for (walk = file->ranges; walk; walk = walk->next) {
134 if (walk->start > offset)
137 if (walk->stop >= offset + count) {
146 * gst_sparse_file_new:
148 * Make a new #GstSparseFile
150 * Returns: a new #GstSparseFile, gst_sparse_file_free() after usage.
155 gst_sparse_file_new (void)
157 GstSparseFile *result;
159 result = g_slice_new0 (GstSparseFile);
160 result->current_pos = 0;
161 result->ranges = NULL;
162 result->n_ranges = 0;
168 * gst_sparse_file_set_fd:
169 * @file: a #GstSparseFile
170 * @fd: a file descriptor
172 * Store the data for @file in the file represented with @fd.
174 * Returns: %TRUE when @fd could be set
179 gst_sparse_file_set_fd (GstSparseFile * file, gint fd)
181 g_return_val_if_fail (file != NULL, FALSE);
182 g_return_val_if_fail (fd != 0, FALSE);
184 file->file = fdopen (fd, "wb+");
187 return file->file != NULL;
191 * gst_sparse_file_clear:
192 * @file: a #GstSparseFile
194 * Clear all the ranges in @file.
197 gst_sparse_file_clear (GstSparseFile * file)
199 g_return_if_fail (file != NULL);
203 file->file = fdopen (file->fd, "wb+");
205 g_slice_free_chain (GstSparseRange, file->ranges, next);
206 file->current_pos = 0;
212 * gst_sparse_file_free:
213 * @file: a #GstSparseFile
215 * Free the memory used by @file.
220 gst_sparse_file_free (GstSparseFile * file)
222 g_return_if_fail (file != NULL);
228 g_slice_free_chain (GstSparseRange, file->ranges, next);
229 g_slice_free (GstSparseFile, file);
233 * gst_sparse_file_write:
234 * @file: a #GstSparseFile
235 * @offset: the offset
237 * @count: amount of bytes
238 * @available: amount of bytes already available
241 * Write @count bytes from @data to @file at @offset.
243 * If @available is not %NULL, it will be updated with the amount of
244 * data already available after the last written byte.
246 * Returns: The number of bytes written or 0 on error.
251 gst_sparse_file_write (GstSparseFile * file, gsize offset, gconstpointer data,
252 gsize count, gsize * available, GError ** error)
254 GstSparseRange *range, *next;
257 g_return_val_if_fail (file != NULL, 0);
258 g_return_val_if_fail (count != 0, 0);
261 if (file->current_pos != offset) {
262 GST_DEBUG ("seeking to %" G_GSIZE_FORMAT, offset);
263 if (FSEEK_FILE (file->file, offset))
266 if (fwrite (data, count, 1, file->file) != 1)
270 file->current_pos = offset + count;
272 /* update the new stop position in the range */
273 range = get_write_range (file, offset);
274 stop = offset + count;
275 range->stop = MAX (range->stop, stop);
277 /* see if we can merge with next region */
278 while ((next = range->next)) {
279 if (next->start > range->stop)
282 GST_DEBUG ("merging range %" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT ", next %"
283 G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT, range->start, range->stop,
284 next->start, next->stop);
286 range->stop = MAX (next->stop, range->stop);
287 range->next = next->next;
289 if (file->write_range == next)
290 file->write_range = NULL;
291 if (file->read_range == next)
292 file->read_range = NULL;
293 g_slice_free (GstSparseRange, next);
297 *available = range->stop - stop;
304 g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
305 gst_sparse_file_io_error_from_errno (errno), "Error writing file: %s",
312 * gst_sparse_file_read:
313 * @file: a #GstSparseFile
314 * @offset: the offset
316 * @count: amount of bytes
317 * @remaining: amount of bytes remaining
320 * Read @count bytes from @file at @offset into @data.
322 * On error, @error will be set. If there are no @count bytes available
323 * at @offset, %GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK is returned.
325 * @remaining will be set to the amount of bytes remaining in the read
328 * Returns: The number of bytes read of 0 on error.
333 gst_sparse_file_read (GstSparseFile * file, gsize offset, gpointer data,
334 gsize count, gsize * remaining, GError ** error)
336 GstSparseRange *range;
339 g_return_val_if_fail (file != NULL, 0);
340 g_return_val_if_fail (count != 0, 0);
342 if ((range = get_read_range (file, offset, count)) == NULL)
346 if (file->current_pos != offset) {
347 GST_DEBUG ("seeking from %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
348 file->current_pos, offset);
349 if (FSEEK_FILE (file->file, offset))
352 res = fread (data, 1, count, file->file);
353 if (G_UNLIKELY (res < count))
357 file->current_pos = offset + res;
360 *remaining = range->stop - file->current_pos;
367 g_set_error_literal (error, GST_SPARSE_FILE_IO_ERROR,
368 GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK, "Offset not written to file yet");
373 if (ferror (file->file)) {
374 g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
375 gst_sparse_file_io_error_from_errno (errno), "Error reading file: %s",
377 } else if (feof (file->file)) {
385 * gst_sparse_file_n_ranges:
386 * @file: a #GstSparseFile
388 * Get the number of ranges that are written in @file.
390 * Returns: the number of written ranges.
395 gst_sparse_file_n_ranges (GstSparseFile * file)
397 g_return_val_if_fail (file != NULL, 0);
399 return file->n_ranges;
403 * gst_sparse_file_get_range_before:
404 * @file: a #GstSparseFile
405 * @offset: the range offset
406 * @start: result start
409 * Get the start and stop offset of the range containing data before or
412 * Returns: %TRUE if the range with data before @offset exists.
417 gst_sparse_file_get_range_before (GstSparseFile * file, gsize offset,
418 gsize * start, gsize * stop)
420 GstSparseRange *walk, *result = NULL;
422 g_return_val_if_fail (file != NULL, FALSE);
424 for (walk = file->ranges; walk; walk = walk->next) {
425 GST_DEBUG ("start %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
427 if (walk->start > offset)
430 if (walk->start <= offset)
436 *start = result->start;
438 *stop = result->stop;
440 return result != NULL;
444 * gst_sparse_file_get_range_after:
445 * @file: a #GstSparseFile
446 * @offset: the range offset
447 * @start: result start
450 * Get the start and stop offset of the range containing data after or
453 * Returns: %TRUE if the range with data after @offset exists.
458 gst_sparse_file_get_range_after (GstSparseFile * file, gsize offset,
459 gsize * start, gsize * stop)
461 GstSparseRange *walk, *result = NULL;
463 g_return_val_if_fail (file != NULL, FALSE);
465 for (walk = file->ranges; walk; walk = walk->next) {
466 GST_DEBUG ("stop %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
468 if (walk->stop > offset) {
475 *start = result->start;
477 *stop = result->stop;
479 return result != NULL;
482 /* we don't want to rely on libgio just for g_io_error_from_errno() */
483 static GstSparseFileIOErrorEnum
484 gst_sparse_file_io_error_from_errno (gint err_no)
489 return GST_SPARSE_FILE_IO_ERROR_EXISTS;
495 return GST_SPARSE_FILE_IO_ERROR_IS_DIRECTORY;
501 return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
507 return GST_SPARSE_FILE_IO_ERROR_FILENAME_TOO_LONG;
513 return GST_SPARSE_FILE_IO_ERROR_NOT_FOUND;
519 return GST_SPARSE_FILE_IO_ERROR_NOT_DIRECTORY;
525 return GST_SPARSE_FILE_IO_ERROR_READ_ONLY;
531 return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_LINKS;
537 return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
543 return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
549 return GST_SPARSE_FILE_IO_ERROR_INVALID_ARGUMENT;
555 return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
561 return GST_SPARSE_FILE_IO_ERROR_CANCELLED;
565 /* ENOTEMPTY == EEXIST on AIX for backward compatibility reasons */
566 #if defined (ENOTEMPTY) && (!defined (EEXIST) || (ENOTEMPTY != EEXIST))
568 return GST_SPARSE_FILE_IO_ERROR_NOT_EMPTY;
574 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
578 /* EOPNOTSUPP == ENOTSUP on Linux, but POSIX considers them distinct */
579 #if defined (EOPNOTSUPP) && (!defined (ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
581 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
585 #ifdef EPROTONOSUPPORT
586 case EPROTONOSUPPORT:
587 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
591 #ifdef ESOCKTNOSUPPORT
592 case ESOCKTNOSUPPORT:
593 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
599 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
605 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
611 return GST_SPARSE_FILE_IO_ERROR_TIMED_OUT;
617 return GST_SPARSE_FILE_IO_ERROR_BUSY;
623 return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
627 /* EWOULDBLOCK == EAGAIN on most systems, but POSIX considers them distinct */
628 #if defined (EAGAIN) && (!defined (EWOULDBLOCK) || (EWOULDBLOCK != EAGAIN))
630 return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
636 return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_OPEN_FILES;
642 return GST_SPARSE_FILE_IO_ERROR_ADDRESS_IN_USE;
648 return GST_SPARSE_FILE_IO_ERROR_HOST_UNREACHABLE;
654 return GST_SPARSE_FILE_IO_ERROR_NETWORK_UNREACHABLE;
660 return GST_SPARSE_FILE_IO_ERROR_CONNECTION_REFUSED;
666 return GST_SPARSE_FILE_IO_ERROR_BROKEN_PIPE;
671 return GST_SPARSE_FILE_IO_ERROR_FAILED;