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
40 #define FSEEK_FILE(file,offset) (fseeko (file, (off_t) offset, SEEK_SET) != 0)
41 #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
42 #define FSEEK_FILE(file,offset) (lseek (fileno (file), (off_t) offset, SEEK_SET) == (off_t) -1)
44 #define FSEEK_FILE(file,offset) (fseek (file, offset, SEEK_SET) != 0)
47 #define GST_SPARSE_FILE_IO_ERROR \
48 g_quark_from_static_string("gst-sparse-file-io-error-quark")
50 static GstSparseFileIOErrorEnum
51 gst_sparse_file_io_error_from_errno (gint err_no);
53 typedef struct _GstSparseRange GstSparseRange;
55 struct _GstSparseRange
63 #define RANGE_CONTAINS(r,o) ((r)->start <= (o) && (r)->stop > (o))
71 GstSparseRange *ranges;
74 GstSparseRange *write_range;
75 GstSparseRange *read_range;
78 static GstSparseRange *
79 get_write_range (GstSparseFile * file, gsize offset)
81 GstSparseRange *next, *prev, *result = NULL;
83 if (file->write_range && file->write_range->stop == offset)
84 return file->write_range;
89 if (next->start > offset)
92 if (next->stop >= offset) {
100 result = g_slice_new0 (GstSparseRange);
101 result->start = offset;
102 result->stop = offset;
108 file->ranges = result;
110 file->write_range = result;
111 file->read_range = NULL;
118 static GstSparseRange *
119 get_read_range (GstSparseFile * file, gsize offset, gsize count)
121 GstSparseRange *walk, *result = NULL;
123 if (file->read_range && RANGE_CONTAINS (file->read_range, offset))
124 return file->read_range;
126 for (walk = file->ranges; walk; walk = walk->next) {
127 if (walk->start > offset)
130 if (walk->stop >= offset + count) {
139 * gst_sparse_file_new:
141 * Make a new #GstSparseFile
143 * Returns: a new #GstSparseFile, gst_sparse_file_free() after usage.
148 gst_sparse_file_new (void)
150 GstSparseFile *result;
152 result = g_slice_new0 (GstSparseFile);
153 result->current_pos = 0;
154 result->ranges = NULL;
155 result->n_ranges = 0;
161 * gst_sparse_file_set_fd:
162 * @file: a #GstSparseFile
163 * @fd: a file descriptor
165 * Store the data for @file in the file represented with @fd.
167 * Returns: %TRUE when @fd could be set
172 gst_sparse_file_set_fd (GstSparseFile * file, gint fd)
174 g_return_val_if_fail (file != NULL, FALSE);
175 g_return_val_if_fail (fd != 0, FALSE);
177 file->file = fdopen (fd, "wb+");
180 return file->file != NULL;
184 * gst_sparse_file_clear:
185 * @file: a #GstSparseFile
187 * Clear all the ranges in @file.
190 gst_sparse_file_clear (GstSparseFile * file)
192 g_return_if_fail (file != NULL);
196 file->file = fdopen (file->fd, "wb+");
198 g_slice_free_chain (GstSparseRange, file->ranges, next);
199 file->current_pos = 0;
205 * gst_sparse_file_free:
206 * @file: a #GstSparseFile
208 * Free the memory used by @file.
213 gst_sparse_file_free (GstSparseFile * file)
215 g_return_if_fail (file != NULL);
221 g_slice_free_chain (GstSparseRange, file->ranges, next);
222 g_slice_free (GstSparseFile, file);
226 * gst_sparse_file_write:
227 * @file: a #GstSparseFile
228 * @offset: the offset
230 * @count: amount of bytes
231 * @available: amount of bytes already available
234 * Write @count bytes from @data to @file at @offset.
236 * If @available is not %NULL, it will be updated with the amount of
237 * data already available after the last written byte.
239 * Returns: The number of bytes written or 0 on error.
244 gst_sparse_file_write (GstSparseFile * file, gsize offset, gconstpointer data,
245 gsize count, gsize * available, GError ** error)
247 GstSparseRange *range, *next;
250 g_return_val_if_fail (file != NULL, 0);
251 g_return_val_if_fail (count != 0, 0);
254 if (file->current_pos != offset) {
255 GST_DEBUG ("seeking to %" G_GSIZE_FORMAT, offset);
256 if (FSEEK_FILE (file->file, offset))
259 if (fwrite (data, count, 1, file->file) != 1)
263 file->current_pos = offset + count;
265 /* update the new stop position in the range */
266 range = get_write_range (file, offset);
267 stop = offset + count;
268 range->stop = MAX (range->stop, stop);
270 /* see if we can merge with next region */
271 while ((next = range->next)) {
272 if (next->start > range->stop)
275 GST_DEBUG ("merging range %" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT ", next %"
276 G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT, range->start, range->stop,
277 next->start, next->stop);
279 range->stop = MAX (next->stop, range->stop);
280 range->next = next->next;
282 if (file->write_range == next)
283 file->write_range = NULL;
284 if (file->read_range == next)
285 file->read_range = NULL;
286 g_slice_free (GstSparseRange, next);
290 *available = range->stop - stop;
297 g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
298 gst_sparse_file_io_error_from_errno (errno), "Error writing file: %s",
305 * gst_sparse_file_read:
306 * @file: a #GstSparseFile
307 * @offset: the offset
309 * @count: amount of bytes
310 * @remaining: amount of bytes remaining
313 * Read @count bytes from @file at @offset into @data.
315 * On error, @error will be set. If there are no @count bytes available
316 * at @offset, %GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK is returned.
318 * @remaining will be set to the amount of bytes remaining in the read
321 * Returns: The number of bytes read of 0 on error.
326 gst_sparse_file_read (GstSparseFile * file, gsize offset, gpointer data,
327 gsize count, gsize * remaining, GError ** error)
329 GstSparseRange *range;
332 g_return_val_if_fail (file != NULL, 0);
333 g_return_val_if_fail (count != 0, 0);
335 if ((range = get_read_range (file, offset, count)) == NULL)
339 if (file->current_pos != offset) {
340 GST_DEBUG ("seeking from %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
341 file->current_pos, offset);
342 if (FSEEK_FILE (file->file, offset))
345 res = fread (data, 1, count, file->file);
346 if (G_UNLIKELY (res < count))
350 file->current_pos = offset + res;
353 *remaining = range->stop - file->current_pos;
360 g_set_error_literal (error, GST_SPARSE_FILE_IO_ERROR,
361 GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK, "Offset not written to file yet");
366 if (ferror (file->file)) {
367 g_set_error (error, GST_SPARSE_FILE_IO_ERROR,
368 gst_sparse_file_io_error_from_errno (errno), "Error reading file: %s",
370 } else if (feof (file->file)) {
378 * gst_sparse_file_n_ranges:
379 * @file: a #GstSparseFile
381 * Get the number of ranges that are written in @file.
383 * Returns: the number of written ranges.
388 gst_sparse_file_n_ranges (GstSparseFile * file)
390 g_return_val_if_fail (file != NULL, 0);
392 return file->n_ranges;
396 * gst_sparse_file_get_range_before:
397 * @file: a #GstSparseFile
398 * @offset: the range offset
399 * @start: result start
402 * Get the start and stop offset of the range containing data before or
405 * Returns: %TRUE if the range with data before @offset exists.
410 gst_sparse_file_get_range_before (GstSparseFile * file, gsize offset,
411 gsize * start, gsize * stop)
413 GstSparseRange *walk, *result = NULL;
415 g_return_val_if_fail (file != NULL, FALSE);
417 for (walk = file->ranges; walk; walk = walk->next) {
418 GST_DEBUG ("start %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
420 if (walk->start > offset)
423 if (walk->start <= offset)
429 *start = result->start;
431 *stop = result->stop;
433 return result != NULL;
437 * gst_sparse_file_get_range_after:
438 * @file: a #GstSparseFile
439 * @offset: the range offset
440 * @start: result start
443 * Get the start and stop offset of the range containing data after or
446 * Returns: %TRUE if the range with data after @offset exists.
451 gst_sparse_file_get_range_after (GstSparseFile * file, gsize offset,
452 gsize * start, gsize * stop)
454 GstSparseRange *walk, *result = NULL;
456 g_return_val_if_fail (file != NULL, FALSE);
458 for (walk = file->ranges; walk; walk = walk->next) {
459 GST_DEBUG ("stop %" G_GSIZE_FORMAT " > %" G_GSIZE_FORMAT,
461 if (walk->stop > offset) {
468 *start = result->start;
470 *stop = result->stop;
472 return result != NULL;
475 /* we don't want to rely on libgio just for g_io_error_from_errno() */
476 static GstSparseFileIOErrorEnum
477 gst_sparse_file_io_error_from_errno (gint err_no)
482 return GST_SPARSE_FILE_IO_ERROR_EXISTS;
488 return GST_SPARSE_FILE_IO_ERROR_IS_DIRECTORY;
494 return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
500 return GST_SPARSE_FILE_IO_ERROR_FILENAME_TOO_LONG;
506 return GST_SPARSE_FILE_IO_ERROR_NOT_FOUND;
512 return GST_SPARSE_FILE_IO_ERROR_NOT_DIRECTORY;
518 return GST_SPARSE_FILE_IO_ERROR_READ_ONLY;
524 return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_LINKS;
530 return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
536 return GST_SPARSE_FILE_IO_ERROR_NO_SPACE;
542 return GST_SPARSE_FILE_IO_ERROR_INVALID_ARGUMENT;
548 return GST_SPARSE_FILE_IO_ERROR_PERMISSION_DENIED;
554 return GST_SPARSE_FILE_IO_ERROR_CANCELLED;
558 /* ENOTEMPTY == EEXIST on AIX for backward compatibility reasons */
559 #if defined (ENOTEMPTY) && (!defined (EEXIST) || (ENOTEMPTY != EEXIST))
561 return GST_SPARSE_FILE_IO_ERROR_NOT_EMPTY;
567 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
571 /* EOPNOTSUPP == ENOTSUP on Linux, but POSIX considers them distinct */
572 #if defined (EOPNOTSUPP) && (!defined (ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
574 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
578 #ifdef EPROTONOSUPPORT
579 case EPROTONOSUPPORT:
580 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
584 #ifdef ESOCKTNOSUPPORT
585 case ESOCKTNOSUPPORT:
586 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
592 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
598 return GST_SPARSE_FILE_IO_ERROR_NOT_SUPPORTED;
604 return GST_SPARSE_FILE_IO_ERROR_TIMED_OUT;
610 return GST_SPARSE_FILE_IO_ERROR_BUSY;
616 return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
620 /* EWOULDBLOCK == EAGAIN on most systems, but POSIX considers them distinct */
621 #if defined (EAGAIN) && (!defined (EWOULDBLOCK) || (EWOULDBLOCK != EAGAIN))
623 return GST_SPARSE_FILE_IO_ERROR_WOULD_BLOCK;
629 return GST_SPARSE_FILE_IO_ERROR_TOO_MANY_OPEN_FILES;
635 return GST_SPARSE_FILE_IO_ERROR_ADDRESS_IN_USE;
641 return GST_SPARSE_FILE_IO_ERROR_HOST_UNREACHABLE;
647 return GST_SPARSE_FILE_IO_ERROR_NETWORK_UNREACHABLE;
653 return GST_SPARSE_FILE_IO_ERROR_CONNECTION_REFUSED;
659 return GST_SPARSE_FILE_IO_ERROR_BROKEN_PIPE;
664 return GST_SPARSE_FILE_IO_ERROR_FAILED;