backed_block.c revision b55dceea986ab24f8b836b5116b389ed619c816e
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <errno.h>
19#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "backed_block.h"
24
25struct backed_block {
26	unsigned int block;
27	unsigned int len;
28	enum backed_block_type type;
29	union {
30		struct {
31			void *data;
32		} data;
33		struct {
34			char *filename;
35			int64_t offset;
36		} file;
37		struct {
38			uint32_t val;
39		} fill;
40	};
41	struct backed_block *next;
42};
43
44struct backed_block_list {
45	struct backed_block *data_blocks;
46	struct backed_block *last_used;
47};
48
49struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
50{
51	return bbl->data_blocks;
52}
53
54struct backed_block *backed_block_iter_next(struct backed_block *bb)
55{
56	return bb->next;
57}
58
59unsigned int backed_block_len(struct backed_block *bb)
60{
61	return bb->len;
62}
63
64unsigned int backed_block_block(struct backed_block *bb)
65{
66	return bb->block;
67}
68
69void *backed_block_data(struct backed_block *bb)
70{
71	assert(bb->type == BACKED_BLOCK_DATA);
72	return bb->data.data;
73}
74
75const char *backed_block_filename(struct backed_block *bb)
76{
77	assert(bb->type == BACKED_BLOCK_FILE);
78	return bb->file.filename;
79}
80
81int64_t backed_block_file_offset(struct backed_block *bb)
82{
83	assert(bb->type == BACKED_BLOCK_FILE);
84	return bb->file.offset;
85}
86
87uint32_t backed_block_fill_val(struct backed_block *bb)
88{
89	assert(bb->type == BACKED_BLOCK_FILL);
90	return bb->fill.val;
91}
92
93enum backed_block_type backed_block_type(struct backed_block *bb)
94{
95	return bb->type;
96}
97
98struct backed_block_list *backed_block_list_new(void)
99{
100	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
101
102	return b;
103}
104
105void backed_block_list_destroy(struct backed_block_list *bbl)
106{
107	if (bbl->data_blocks) {
108		struct backed_block *bb = bbl->data_blocks;
109		while (bb) {
110			struct backed_block *next = bb->next;
111			if (bb->type == BACKED_BLOCK_FILE) {
112				free(bb->file.filename);
113			}
114
115			free(bb);
116			bb = next;
117		}
118	}
119
120	free(bbl);
121}
122
123static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
124{
125	struct backed_block *bb;
126
127	if (bbl->data_blocks == NULL) {
128		bbl->data_blocks = new_bb;
129		return 0;
130	}
131
132	if (bbl->data_blocks->block > new_bb->block) {
133		new_bb->next = bbl->data_blocks;
134		bbl->data_blocks = new_bb;
135		return 0;
136	}
137
138	/* Optimization: blocks are mostly queued in sequence, so save the
139	   pointer to the last bb that was added, and start searching from
140	   there if the next block number is higher */
141	if (bbl->last_used && new_bb->block > bbl->last_used->block)
142		bb = bbl->last_used;
143	else
144		bb = bbl->data_blocks;
145	bbl->last_used = new_bb;
146
147	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
148		;
149
150	if (bb->next == NULL) {
151		bb->next = new_bb;
152	} else {
153		new_bb->next = bb->next;
154		bb->next = new_bb;
155	}
156
157	return 0;
158}
159
160/* Queues a fill block of memory to be written to the specified data blocks */
161int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
162		unsigned int len, unsigned int block)
163{
164	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
165	if (bb == NULL) {
166		return -ENOMEM;
167	}
168
169	bb->block = block;
170	bb->len = len;
171	bb->type = BACKED_BLOCK_FILL;
172	bb->fill.val = fill_val;
173	bb->next = NULL;
174
175	return queue_bb(bbl, bb);
176}
177
178/* Queues a block of memory to be written to the specified data blocks */
179int backed_block_add_data(struct backed_block_list *bbl, void *data,
180		unsigned int len, unsigned int block)
181{
182	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
183	if (bb == NULL) {
184		return -ENOMEM;
185	}
186
187	bb->block = block;
188	bb->len = len;
189	bb->type = BACKED_BLOCK_DATA;
190	bb->data.data = data;
191	bb->next = NULL;
192
193	return queue_bb(bbl, bb);
194}
195
196/* Queues a chunk of a file on disk to be written to the specified data blocks */
197int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
198		int64_t offset, unsigned int len, unsigned int block)
199{
200	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
201	if (bb == NULL) {
202		return -ENOMEM;
203	}
204
205	bb->block = block;
206	bb->len = len;
207	bb->type = BACKED_BLOCK_FILE;
208	bb->file.filename = strdup(filename);
209	bb->file.offset = offset;
210	bb->next = NULL;
211
212	return queue_bb(bbl, bb);
213}
214