aboutsummaryrefslogtreecommitdiff
path: root/lib/io/src/mem.c
blob: 7150d1f588b30258709763cc9ff04f921eeb87fa (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
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
 * mem.c
 *
 * Copyright (C) 2023 David Oberhollenzer <goliath@infraroot.at>
 */
#include "config.h"
#include "io/mem.h"
#include "compat.h"
#include "sqfs/io.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

typedef struct {
	sqfs_istream_t base;

	sqfs_u8 *buffer;
	size_t bufsz;

	const void *data;
	size_t size;
	size_t offset;
	size_t visible;

	char *name;
} mem_istream_t;

static int mem_get_buffered_data(sqfs_istream_t *strm, const sqfs_u8 **out,
				 size_t *size, size_t want)
{
	mem_istream_t *mem = (mem_istream_t *)strm;
	size_t have = mem->size - mem->offset;

	if (have > mem->bufsz)
		have = mem->bufsz;

	if (want > have)
		want = have;

	if (mem->visible == 0 || mem->visible < want) {
		memcpy(mem->buffer + mem->visible,
		       (const char *)mem->data + mem->offset + mem->visible,
		       have - mem->visible);
		mem->visible = have;
	}

	*out = mem->buffer;
	*size = mem->visible;
	return (mem->visible == 0) ? 1 : 0;
}

static void mem_advance_buffer(sqfs_istream_t *strm, size_t count)
{
	mem_istream_t *mem = (mem_istream_t *)strm;

	assert(count <= mem->visible);

	if (count > 0 && count < mem->visible)
		memmove(mem->buffer, mem->buffer + count, mem->visible - count);

	mem->offset += count;
	mem->visible -= count;

	if (mem->visible < mem->bufsz) {
		memset(mem->buffer + mem->visible, 0,
		       mem->bufsz - mem->visible);
	}
}

static const char *mem_in_get_filename(sqfs_istream_t *strm)
{
	return ((mem_istream_t *)strm)->name;
}

static void mem_in_destroy(sqfs_object_t *obj)
{
	free(((mem_istream_t *)obj)->buffer);
	free(((mem_istream_t *)obj)->name);
	free(obj);
}

sqfs_istream_t *istream_memory_create(const char *name, size_t bufsz,
				      const void *data, size_t size)
{
	mem_istream_t *mem = calloc(1, sizeof(*mem));
	sqfs_istream_t *strm = (sqfs_istream_t *)mem;

	if (mem == NULL)
		return NULL;

	sqfs_object_init(mem, mem_in_destroy, NULL);

	mem->name = strdup(name);
	if (mem->name == NULL)
		return sqfs_drop(mem);

	mem->buffer = malloc(bufsz);
	if (mem->buffer == NULL)
		return sqfs_drop(mem);

	mem->data = data;
	mem->size = size;
	mem->bufsz = bufsz;
	strm->get_buffered_data = mem_get_buffered_data;
	strm->advance_buffer = mem_advance_buffer;
	strm->get_filename = mem_in_get_filename;
	return strm;
}