1 /*
2  * Copyright (c) 2003-2012 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * Basic I/O abstraction routines.
28  */
29 
30 #include <agar/core/core.h>
31 
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdarg.h>
35 
36 static AG_Object errorMgr;
37 
38 void
AG_DataSourceInitSubsystem(void)39 AG_DataSourceInitSubsystem(void)
40 {
41 	AG_ObjectInitStatic(&errorMgr, NULL);
42 }
43 
44 void
AG_DataSourceDestroySubsystem(void)45 AG_DataSourceDestroySubsystem(void)
46 {
47 	AG_ObjectDestroy(&errorMgr);
48 }
49 
50 /* Assign an error callback routine to a data source. */
51 void
AG_DataSourceSetErrorFn(AG_DataSource * ds,AG_EventFn fn,const char * fmt,...)52 AG_DataSourceSetErrorFn(AG_DataSource *ds, AG_EventFn fn, const char *fmt, ...)
53 {
54 	AG_ObjectLock(&errorMgr);
55 	ds->errorFn = AG_SetEvent(&errorMgr, NULL, fn, NULL);
56 	AG_EVENT_GET_ARGS(ds->errorFn, fmt);
57 	AG_ObjectUnlock(&errorMgr);
58 }
59 
60 /* Raise a data source exception. */
61 void
AG_DataSourceError(AG_DataSource * ds,const char * fmt,...)62 AG_DataSourceError(AG_DataSource *ds, const char *fmt, ...)
63 {
64 	static char msg[256];
65 	va_list args;
66 
67 	if (fmt != NULL) {
68 		va_start(args, fmt);
69 		Vsnprintf(msg, sizeof(msg), fmt, args);
70 		va_end(args);
71 	} else {
72 		Strlcpy(msg, AG_GetError(), sizeof(msg));
73 	}
74 
75 	AG_ObjectLock(&errorMgr);
76 	AG_PostEventByPtr(NULL, &errorMgr, ds->errorFn, "%s", msg);
77 	AG_ObjectUnlock(&errorMgr);
78 }
79 
80 /* Enable checking of debugging information. */
81 void
AG_DataSourceSetDebug(AG_DataSource * ds,int flag)82 AG_DataSourceSetDebug(AG_DataSource *ds, int flag)
83 {
84 	ds->debug = flag;
85 }
86 
87 /* Write type identifier for type safety checks. */
88 void
AG_WriteTypeCode(AG_DataSource * ds,Uint32 type)89 AG_WriteTypeCode(AG_DataSource *ds, Uint32 type)
90 {
91 	Uint32 i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) :
92 	                                                 AG_SwapLE32(type);
93 
94 	if (AG_Write(ds, &i, sizeof(i)) != 0)
95 		AG_DataSourceError(ds, NULL);
96 }
97 
98 /* Write type identifier for type safety checks (offset). */
99 void
AG_WriteTypeCodeAt(AG_DataSource * ds,Uint32 type,off_t offs)100 AG_WriteTypeCodeAt(AG_DataSource *ds, Uint32 type, off_t offs)
101 {
102 	Uint32 i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) :
103 	                                                 AG_SwapLE32(type);
104 
105 	if (AG_WriteAt(ds, &i, sizeof(i), offs) != 0)
106 		AG_DataSourceError(ds, NULL);
107 }
108 
109 /* Write type identifier for type safety checks (error-check). */
110 int
AG_WriteTypeCodeE(AG_DataSource * ds,Uint32 type)111 AG_WriteTypeCodeE(AG_DataSource *ds, Uint32 type)
112 {
113 	Uint32 i;
114 
115 	i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) :
116 	                                          AG_SwapLE32(type);
117 
118 	return AG_Write(ds, &i, sizeof(i));
119 }
120 
121 /* Check type identifier for type safety checks (error-check). */
122 int
AG_CheckTypeCode(AG_DataSource * ds,Uint32 type)123 AG_CheckTypeCode(AG_DataSource *ds, Uint32 type)
124 {
125 	Uint32 i;
126 
127 	if (AG_Read(ds, &i, sizeof(i)) != 0) {
128 		AG_SetError("Reading type ID: %s", AG_GetError());
129 		return (-1);
130 	}
131 	i = ((ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(i) :
132 	                                           AG_SwapLE32(i));
133 	return (i == type) ? 0 : -1;
134 }
135 
136 /*
137  * No-ops
138  */
139 static int
WriteNotSup(AG_DataSource * ds,const void * buf,size_t size,size_t * rv)140 WriteNotSup(AG_DataSource *ds, const void *buf, size_t size, size_t *rv)
141 {
142 	AG_SetError(_("Operation not supported"));
143 	return (-1);
144 }
145 static int
WriteAtNotSup(AG_DataSource * ds,const void * buf,size_t size,off_t pos,size_t * rv)146 WriteAtNotSup(AG_DataSource *ds, const void *buf, size_t size, off_t pos, size_t *rv)
147 {
148 	AG_SetError(_("Operation not supported"));
149 	return (-1);
150 }
151 
152 #ifdef AG_NETWORK
153 static int
ReadAtNotSup(AG_DataSource * ds,void * buf,size_t size,off_t pos,size_t * rv)154 ReadAtNotSup(AG_DataSource *ds, void *buf, size_t size, off_t pos, size_t *rv)
155 {
156 	AG_SetError(_("Operation not supported"));
157 	return (-1);
158 }
159 static off_t
TellNotSup(AG_DataSource * ds)160 TellNotSup(AG_DataSource *ds)
161 {
162 	return (0);
163 }
164 static int
SeekNotSup(AG_DataSource * ds,off_t offs,enum ag_seek_mode mode)165 SeekNotSup(AG_DataSource *ds, off_t offs, enum ag_seek_mode mode)
166 {
167 	AG_SetError(_("Seek not supported by data source"));
168 	return (-1);
169 }
170 #endif /* AG_NETWORK */
171 
172 /*
173  * File operations.
174  */
175 static int
FileRead(AG_DataSource * ds,void * buf,size_t size,size_t * rv)176 FileRead(AG_DataSource *ds, void *buf, size_t size, size_t *rv)
177 {
178 	FILE *f = AG_FILE_SOURCE(ds)->file;
179 
180 	clearerr(f);
181 	*rv = fread(buf, 1, size, f);
182 	if (*rv < size && ferror(f)) {
183 		AG_SetError(_("Read error"));
184 		return (-1);
185 	}
186 	return (0);
187 }
188 static int
FileReadAt(AG_DataSource * ds,void * buf,size_t size,off_t pos,size_t * rv)189 FileReadAt(AG_DataSource *ds, void *buf, size_t size, off_t pos, size_t *rv)
190 {
191 	FILE *f = AG_FILE_SOURCE(ds)->file;
192 	long savedPos = ftell(f);
193 
194 	if (fseek(f, pos, SEEK_SET) == -1) { goto fail_seek; }
195 	clearerr(f);
196 	*rv = fread(buf, 1, size, f);
197 	if (*rv < size && ferror(f)) {
198 		if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; }
199 		AG_SetError(_("Read Error"));
200 		return (-1);
201 	}
202 	if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; }
203 	return (0);
204 fail_seek:
205 	AG_SetError("fseek failed");
206 	return (-1);
207 }
208 static int
FileWrite(AG_DataSource * ds,const void * buf,size_t size,size_t * rv)209 FileWrite(AG_DataSource *ds, const void *buf, size_t size, size_t *rv)
210 {
211 	FILE *f = AG_FILE_SOURCE(ds)->file;
212 
213 	clearerr(f);
214 	*rv = fwrite(buf, 1, size, f);
215 	if (*rv < size && ferror(f)) {
216 		AG_SetError(_("Write error"));
217 		return (-1);
218 	}
219 	return (0);
220 }
221 static int
FileWriteAt(AG_DataSource * ds,const void * buf,size_t size,off_t pos,size_t * rv)222 FileWriteAt(AG_DataSource *ds, const void *buf, size_t size, off_t pos,
223     size_t *rv)
224 {
225 	FILE *f = AG_FILE_SOURCE(ds)->file;
226 	long savedPos = ftell(f);
227 
228 	if (fseek(f, pos, SEEK_SET) == -1) { goto fail_seek; }
229 	clearerr(f);
230 	*rv = fwrite(buf, 1, size, f);
231 	if (*rv < size && ferror(f)) {
232 		if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; }
233 		AG_SetError(_("Write Error"));
234 		return (-1);
235 	}
236 	if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; }
237 	return (0);
238 fail_seek:
239 	AG_SetError("fseek failed");
240 	return (-1);
241 }
242 static off_t
FileTell(AG_DataSource * ds)243 FileTell(AG_DataSource *ds)
244 {
245 	return ftell(AG_FILE_SOURCE(ds)->file);
246 }
247 static int
FileSeek(AG_DataSource * ds,off_t offs,enum ag_seek_mode mode)248 FileSeek(AG_DataSource *ds, off_t offs, enum ag_seek_mode mode)
249 {
250 	FILE *f = AG_FILE_SOURCE(ds)->file;
251 
252 	if (fseek(f, (long)offs,
253 	    (mode == AG_SEEK_SET) ? SEEK_SET :
254 	    (mode == AG_SEEK_CUR) ? SEEK_CUR :
255 	    SEEK_END) == -1) {
256 		AG_SetError("fseek failed");
257 		return (-1);
258 	}
259 	return (0);
260 }
261 void
AG_CloseFile(AG_DataSource * ds)262 AG_CloseFile(AG_DataSource *ds)
263 {
264 	AG_FileSource *fs = AG_FILE_SOURCE(ds);
265 
266 	fclose(fs->file);
267 	Free(fs->path);
268 	AG_DataSourceDestroy(ds);
269 }
270 
271 /*
272  * Memory operations.
273  */
274 static __inline__ int
CoreLimitBounds(AG_CoreSource * cs,off_t pos,size_t sizeReq,size_t * size)275 CoreLimitBounds(AG_CoreSource *cs, off_t pos, size_t sizeReq, size_t *size)
276 {
277 	if (pos+sizeReq > cs->size) {
278 		*size = cs->size - cs->offs;
279 	} else {
280 		*size = sizeReq;
281 	}
282 	return (0);
283 }
284 static int
CoreRead(AG_DataSource * ds,void * buf,size_t sizeReq,size_t * rv)285 CoreRead(AG_DataSource *ds, void *buf, size_t sizeReq, size_t *rv)
286 {
287 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
288 	size_t size;
289 
290 	if (CoreLimitBounds(cs, cs->offs, sizeReq, &size) == -1) {
291 		return (-1);
292 	}
293 	memcpy(buf, &cs->data[cs->offs], size);
294 	*rv = size;
295 	cs->offs += size;
296 	return (0);
297 }
298 static int
CoreReadAt(AG_DataSource * ds,void * buf,size_t sizeReq,off_t pos,size_t * rv)299 CoreReadAt(AG_DataSource *ds, void *buf, size_t sizeReq, off_t pos, size_t *rv)
300 {
301 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
302 	size_t size;
303 
304 	if (CoreLimitBounds(cs, pos, sizeReq, &size) == -1) {
305 		return (-1);
306 	}
307 	memcpy(buf, &cs->data[pos], size);
308 	*rv = size;
309 	return (0);
310 }
311 static int
CoreWrite(AG_DataSource * ds,const void * buf,size_t sizeReq,size_t * rv)312 CoreWrite(AG_DataSource *ds, const void *buf, size_t sizeReq, size_t *rv)
313 {
314 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
315 	size_t size;
316 
317 	if (CoreLimitBounds(cs, cs->offs, sizeReq, &size) == -1) {
318 		return (-1);
319 	}
320 	memcpy(&cs->data[cs->offs], buf, size);
321 	*rv = size;
322 	cs->offs += size;
323 	return (0);
324 }
325 static int
CoreAutoWrite(AG_DataSource * ds,const void * buf,size_t size,size_t * rv)326 CoreAutoWrite(AG_DataSource *ds, const void *buf, size_t size, size_t *rv)
327 {
328 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
329 	Uint8 *dataNew;
330 
331 	if (cs->offs+size > cs->size) {
332 		if ((dataNew = TryRealloc(cs->data, (cs->offs+size))) == NULL) {
333 			return (-1);
334 		}
335 		cs->data = dataNew;
336 	}
337 	memcpy(&cs->data[cs->offs], buf, size);
338 	cs->size += size;
339 	cs->offs += size;
340 	*rv = size;
341 	return (0);
342 }
343 static int
CoreWriteAt(AG_DataSource * ds,const void * buf,size_t sizeReq,off_t pos,size_t * rv)344 CoreWriteAt(AG_DataSource *ds, const void *buf, size_t sizeReq, off_t pos,
345     size_t *rv)
346 {
347 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
348 	size_t size;
349 
350 	if (CoreLimitBounds(cs, pos, sizeReq, &size) == -1) {
351 		return (-1);
352 	}
353 	memcpy(&cs->data[pos], buf, size);
354 	*rv = size;
355 	return (0);
356 }
357 static int
CoreAutoWriteAt(AG_DataSource * ds,const void * buf,size_t size,off_t pos,size_t * rv)358 CoreAutoWriteAt(AG_DataSource *ds, const void *buf, size_t size, off_t pos,
359     size_t *rv)
360 {
361 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
362 	Uint8 *dataNew;
363 
364 	if (pos < 0) {
365 		AG_SetError("Bad offset");
366 		return (-1);
367 	}
368 	if (pos+size > cs->size) {
369 		if ((dataNew = TryRealloc(cs->data, (pos+size))) == NULL) {
370 			return (-1);
371 		}
372 		cs->data = dataNew;
373 		cs->size = pos+size;
374 	}
375 	memcpy(&cs->data[pos], buf, size);
376 	*rv = size;
377 	return (0);
378 }
379 static off_t
CoreTell(AG_DataSource * ds)380 CoreTell(AG_DataSource *ds)
381 {
382 	return AG_CORE_SOURCE(ds)->offs;
383 }
384 static int
CoreSeek(AG_DataSource * ds,off_t offs,enum ag_seek_mode mode)385 CoreSeek(AG_DataSource *ds, off_t offs, enum ag_seek_mode mode)
386 {
387 	AG_CoreSource *cs = AG_CORE_SOURCE(ds);
388 	off_t nOffs;
389 
390 	switch (mode) {
391 	case AG_SEEK_SET:
392 		nOffs = offs;
393 		break;
394 	case AG_SEEK_CUR:
395 		nOffs = cs->offs + offs;
396 		break;
397 	case AG_SEEK_END:
398 	default:
399 		nOffs = cs->size - offs;
400 		break;
401 	}
402 	if (nOffs < 0 || nOffs >= cs->size) {
403 		AG_SetError("Bad offset %ld", (long)nOffs);
404 		return (-1);
405 	}
406 	cs->offs = nOffs;
407 	return (0);
408 }
409 void
AG_CloseCore(AG_DataSource * ds)410 AG_CloseCore(AG_DataSource *ds)
411 {
412 	AG_DataSourceDestroy(ds);
413 }
414 void
AG_CloseAutoCore(AG_DataSource * ds)415 AG_CloseAutoCore(AG_DataSource *ds)
416 {
417 	Free(AG_CORE_SOURCE(ds)->data);
418 	AG_DataSourceDestroy(ds);
419 }
420 
421 #ifdef AG_NETWORK
422 /*
423  * Network socket operations
424  */
425 static int
NetSocketRead(AG_DataSource * ds,void * buf,size_t size,size_t * rv)426 NetSocketRead(AG_DataSource *ds, void *buf, size_t size, size_t *rv)
427 {
428 	AG_NetSocketSource *nss = AG_NET_SOCKET_SOURCE(ds);
429 	return AG_NetRead(nss->sock, buf, size, rv);
430 }
431 static int
NetSocketWrite(AG_DataSource * ds,const void * buf,size_t size,size_t * rv)432 NetSocketWrite(AG_DataSource *ds, const void *buf, size_t size, size_t *rv)
433 {
434 	AG_NetSocketSource *nss = AG_NET_SOCKET_SOURCE(ds);
435 	return AG_NetWrite(nss->sock, buf, size, rv);
436 }
437 void
AG_CloseNetSocket(AG_DataSource * ds)438 AG_CloseNetSocket(AG_DataSource *ds)
439 {
440 	AG_DataSourceDestroy(ds);
441 }
442 #endif
443 
444 /* Default error handler */
445 static void
ErrorDefault(AG_Event * event)446 ErrorDefault(AG_Event *event)
447 {
448 	AG_FatalError("Data source error: %s", AG_GetError());
449 }
450 
451 /* Initialize the data source structure. */
452 void
AG_DataSourceInit(AG_DataSource * ds)453 AG_DataSourceInit(AG_DataSource *ds)
454 {
455 	ds->debug = 0;
456 	ds->byte_order = AG_BYTEORDER_BE;
457 	ds->rdLast = 0;
458 	ds->wrLast = 0;
459 	ds->rdTotal = 0;
460 	ds->wrTotal = 0;
461 	ds->read = NULL;
462 	ds->read_at = NULL;
463 	ds->write = NULL;
464 	ds->write_at = NULL;
465 	ds->tell = NULL;
466 	ds->seek = NULL;
467 	ds->close = NULL;
468 	AG_MutexInitRecursive(&ds->lock);
469 	AG_DataSourceSetErrorFn(ds, ErrorDefault, "%p", ds);
470 }
471 
472 /* Close a data source of any type. */
473 void
AG_CloseDataSource(AG_DataSource * ds)474 AG_CloseDataSource(AG_DataSource *ds)
475 {
476 	ds->close(ds);
477 }
478 
479 /* Release the resources allocated by the data source structure. */
480 void
AG_DataSourceDestroy(AG_DataSource * ds)481 AG_DataSourceDestroy(AG_DataSource *ds)
482 {
483 	AG_MutexDestroy(&ds->lock);
484 	Free(ds);
485 }
486 
487 /* Create a data source from a stdio file handle. */
488 AG_DataSource *
AG_OpenFileHandle(FILE * f)489 AG_OpenFileHandle(FILE *f)
490 {
491 	AG_FileSource *fs;
492 
493 	fs = Malloc(sizeof(AG_FileSource));
494 	AG_DataSourceInit(&fs->ds);
495 	fs->path = NULL;
496 	fs->file = f;
497 	fs->ds.read = FileRead;
498 	fs->ds.read_at = FileReadAt;
499 	fs->ds.write = FileWrite;
500 	fs->ds.write_at = FileWriteAt;
501 	fs->ds.tell = FileTell;
502 	fs->ds.seek = FileSeek;
503 	fs->ds.close = AG_CloseFile;
504 	return (&fs->ds);
505 }
506 
507 /* Create a data source from a specified file path. */
508 AG_DataSource *
AG_OpenFile(const char * path,const char * mode)509 AG_OpenFile(const char *path, const char *mode)
510 {
511 	FILE *f;
512 
513 	if ((f = fopen(path, mode)) == NULL) {
514 		AG_SetError(_("Unable to open %s"), path);
515 		return (NULL);
516 	}
517 	return AG_OpenFileHandle(f);
518 }
519 
520 /* Create a data source from a specified chunk of memory. */
521 AG_DataSource *
AG_OpenCore(void * data,size_t size)522 AG_OpenCore(void *data, size_t size)
523 {
524 	AG_CoreSource *cs;
525 
526 	if ((cs = TryMalloc(sizeof(AG_CoreSource))) == NULL) {
527 		return (NULL);
528 	}
529 	AG_DataSourceInit(&cs->ds);
530 	cs->data = (Uint8 *)data;
531 	cs->size = size;
532 	cs->offs = 0;
533 	cs->ds.read = CoreRead;
534 	cs->ds.read_at = CoreReadAt;
535 	cs->ds.write = CoreWrite;
536 	cs->ds.write_at = CoreWriteAt;
537 	cs->ds.tell = CoreTell;
538 	cs->ds.seek = CoreSeek;
539 	cs->ds.close = AG_CloseCore;
540 	return (&cs->ds);
541 }
542 
543 /* Create a data source from a specified chunk of memory (read-only). */
544 AG_DataSource *
AG_OpenConstCore(const void * data,size_t size)545 AG_OpenConstCore(const void *data, size_t size)
546 {
547 	AG_ConstCoreSource *cs;
548 
549 	if ((cs = TryMalloc(sizeof(AG_ConstCoreSource))) == NULL) {
550 		return (NULL);
551 	}
552 	AG_DataSourceInit(&cs->ds);
553 	cs->data = (const Uint8 *)data;
554 	cs->size = size;
555 	cs->offs = 0;
556 	cs->ds.read = CoreRead;
557 	cs->ds.read_at = CoreReadAt;
558 	cs->ds.write = WriteNotSup;
559 	cs->ds.write_at = WriteAtNotSup;
560 	cs->ds.tell = CoreTell;
561 	cs->ds.seek = CoreSeek;
562 	cs->ds.close = AG_CloseCore;
563 	return (&cs->ds);
564 }
565 
566 /* Create a data source using dynamically-allocated memory. */
567 AG_DataSource *
AG_OpenAutoCore(void)568 AG_OpenAutoCore(void)
569 {
570 	AG_CoreSource *cs;
571 
572 	if ((cs = TryMalloc(sizeof(AG_CoreSource))) == NULL) {
573 		return (NULL);
574 	}
575 	AG_DataSourceInit(&cs->ds);
576 	cs->data = NULL;
577 	cs->size = 0;
578 	cs->offs = 0;
579 	cs->ds.read = CoreRead;
580 	cs->ds.read_at = CoreReadAt;
581 	cs->ds.write = CoreAutoWrite;
582 	cs->ds.write_at = CoreAutoWriteAt;
583 	cs->ds.tell = CoreTell;
584 	cs->ds.seek = CoreSeek;
585 	cs->ds.close = AG_CloseAutoCore;
586 	return (&cs->ds);
587 }
588 
589 #ifdef AG_NETWORK
590 /* Create a data source using a network socket. */
591 AG_DataSource *
AG_OpenNetSocket(AG_NetSocket * ns)592 AG_OpenNetSocket(AG_NetSocket *ns)
593 {
594 	AG_NetSocketSource *ss;
595 
596 	if ((ss = TryMalloc(sizeof(AG_NetSocketSource))) == NULL) {
597 		return (NULL);
598 	}
599 	AG_DataSourceInit(&ss->ds);
600 	ss->sock = ns;
601 	ss->ds.read = NetSocketRead;
602 	ss->ds.read_at = ReadAtNotSup;
603 	ss->ds.write = NetSocketWrite;
604 	ss->ds.write_at = WriteAtNotSup;
605 	ss->ds.tell = TellNotSup;
606 	ss->ds.seek = SeekNotSup;
607 	ss->ds.close = AG_CloseNetSocket;
608 	return (&ss->ds);
609 }
610 #endif /* AG_NETWORK */
611 
612 /* Select the byte order of the source. */
613 void
AG_SetByteOrder(AG_DataSource * ds,enum ag_byte_order order)614 AG_SetByteOrder(AG_DataSource *ds, enum ag_byte_order order)
615 {
616 	AG_MutexLock(&ds->lock);
617 	ds->byte_order = order;
618 	AG_MutexUnlock(&ds->lock);
619 }
620 
621 /* Toggle encoding/decoding of debugging data. */
622 void
AG_SetSourceDebug(AG_DataSource * ds,int enable)623 AG_SetSourceDebug(AG_DataSource *ds, int enable)
624 {
625 	AG_MutexLock(&ds->lock);
626 	ds->debug = enable;
627 	AG_MutexUnlock(&ds->lock);
628 }
629 
630 /* Low-level read operation. */
631 int
AG_Read(AG_DataSource * ds,void * ptr,size_t size)632 AG_Read(AG_DataSource *ds, void *ptr, size_t size)
633 {
634 	int rv;
635 	AG_MutexLock(&ds->lock);
636 	rv = ds->read(ds, ptr, size, &ds->rdLast);
637 	ds->rdTotal += ds->rdLast;
638 	if (ds->rdLast < size) {
639 		AG_SetError("Short read");
640 		rv = -1;
641 	}
642 	AG_MutexUnlock(&ds->lock);
643 	return (rv);
644 }
645 
646 /* Low-level read operation (partial reads allowed). */
647 int
AG_ReadP(AG_DataSource * ds,void * ptr,size_t size,size_t * nRead)648 AG_ReadP(AG_DataSource *ds, void *ptr, size_t size, size_t *nRead)
649 {
650 	int rv;
651 	AG_MutexLock(&ds->lock);
652 	rv = ds->read(ds, ptr, size, &ds->rdLast);
653 	ds->rdTotal += ds->rdLast;
654 	if (nRead != NULL) { *nRead = ds->rdLast; }
655 	AG_MutexUnlock(&ds->lock);
656 	return (rv);
657 }
658 
659 /* Low-level read-at-offset operation. */
660 int
AG_ReadAt(AG_DataSource * ds,void * ptr,size_t size,off_t pos)661 AG_ReadAt(AG_DataSource *ds, void *ptr, size_t size, off_t pos)
662 {
663 	int rv;
664 	AG_MutexLock(&ds->lock);
665 	rv = ds->read_at(ds, ptr, size, pos, &ds->rdLast);
666 	ds->rdTotal += ds->rdLast;
667 	if (ds->rdLast < size) {
668 		AG_SetError("Short read");
669 		rv = -1;
670 	}
671 	AG_MutexUnlock(&ds->lock);
672 	return (rv);
673 }
674 
675 /* Low-level read-at-offset operation (partial reads allowed). */
676 int
AG_ReadAtP(AG_DataSource * ds,void * ptr,size_t size,off_t pos,size_t * nRead)677 AG_ReadAtP(AG_DataSource *ds, void *ptr, size_t size, off_t pos, size_t *nRead)
678 {
679 	int rv;
680 	AG_MutexLock(&ds->lock);
681 	rv = ds->read_at(ds, ptr, size, pos, &ds->rdLast);
682 	ds->rdTotal += ds->rdLast;
683 	if (nRead != NULL) { *nRead = ds->rdLast; }
684 	AG_MutexUnlock(&ds->lock);
685 	return (rv);
686 }
687 
688 /* Low-level write operation. */
689 int
AG_Write(AG_DataSource * ds,const void * ptr,size_t size)690 AG_Write(AG_DataSource *ds, const void *ptr, size_t size)
691 {
692 	int rv;
693 	AG_MutexLock(&ds->lock);
694 	rv = ds->write(ds, ptr, size, &ds->wrLast);
695 	ds->wrTotal += ds->wrLast;
696 	if (ds->wrLast < size) {
697 		AG_SetError("Short write");
698 		rv = -1;
699 	}
700 	AG_MutexUnlock(&ds->lock);
701 	return (rv);
702 }
703 
704 /* Low-level write operation (partial writes allowed). */
705 int
AG_WriteP(AG_DataSource * ds,const void * ptr,size_t size,size_t * nWrote)706 AG_WriteP(AG_DataSource *ds, const void *ptr, size_t size, size_t *nWrote)
707 {
708 	int rv;
709 	AG_MutexLock(&ds->lock);
710 	rv = ds->write(ds, ptr, size, &ds->wrLast);
711 	ds->wrTotal += ds->wrLast;
712 	if (nWrote != NULL) { *nWrote = ds->wrLast; }
713 	AG_MutexUnlock(&ds->lock);
714 	return (rv);
715 }
716 
717 /* Low-level write-at-offset operation. */
718 int
AG_WriteAt(AG_DataSource * ds,const void * ptr,size_t size,off_t pos)719 AG_WriteAt(AG_DataSource *ds, const void *ptr, size_t size, off_t pos)
720 {
721 	int rv;
722 	AG_MutexLock(&ds->lock);
723 	rv = ds->write_at(ds, ptr, size, pos, &ds->wrLast);
724 	ds->wrTotal += ds->wrLast;
725 	if (ds->wrLast < size) {
726 		AG_SetError("Short write");
727 		rv = -1;
728 	}
729 	AG_MutexUnlock(&ds->lock);
730 	return (rv);
731 }
732 
733 /* Low-level write-at-offset operation (partial writes allowed) */
734 int
AG_WriteAtP(AG_DataSource * ds,const void * ptr,size_t size,off_t pos,size_t * nWrote)735 AG_WriteAtP(AG_DataSource *ds, const void *ptr, size_t size, off_t pos, size_t *nWrote)
736 {
737 	int rv;
738 	AG_MutexLock(&ds->lock);
739 	rv = ds->write_at(ds, ptr, size, pos, &ds->wrLast);
740 	ds->wrTotal += ds->wrLast;
741 	if (nWrote != NULL) { *nWrote = ds->wrLast; }
742 	AG_MutexUnlock(&ds->lock);
743 	return (rv);
744 }
745 
746