cannam@226: /*
cannam@226:   Copyright 2011-2017 David Robillard <http://drobilla.net>
cannam@226: 
cannam@226:   Permission to use, copy, modify, and/or distribute this software for any
cannam@226:   purpose with or without fee is hereby granted, provided that the above
cannam@226:   copyright notice and this permission notice appear in all copies.
cannam@226: 
cannam@226:   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
cannam@226:   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
cannam@226:   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
cannam@226:   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
cannam@226:   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
cannam@226:   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
cannam@226:   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
cannam@226: */
cannam@226: 
cannam@226: #include "serd_internal.h"
cannam@226: 
cannam@226: static inline SerdStatus
cannam@226: serd_byte_source_page(SerdByteSource* source)
cannam@226: {
cannam@226: 	source->read_head = 0;
cannam@226: 	size_t n_read = source->read_func(
cannam@226: 		source->file_buf, 1, source->page_size, source->stream);
cannam@226: 	if (n_read == 0) {
cannam@226: 		source->file_buf[0] = '\0';
cannam@226: 		return (source->error_func(source->stream)
cannam@226: 		        ? SERD_ERR_UNKNOWN : SERD_FAILURE);
cannam@226: 	} else if (n_read < source->page_size) {
cannam@226: 		source->file_buf[n_read] = '\0';
cannam@226: 	}
cannam@226: 	return SERD_SUCCESS;
cannam@226: }
cannam@226: 
cannam@226: SerdStatus
cannam@226: serd_byte_source_open_source(SerdByteSource*     source,
cannam@226:                              SerdSource          read_func,
cannam@226:                              SerdStreamErrorFunc error_func,
cannam@226:                              void*               stream,
cannam@226:                              size_t              page_size)
cannam@226: {
cannam@226: 	memset(source, '\0', sizeof(*source));
cannam@226: 	source->stream      = stream;
cannam@226: 	source->from_stream = true;
cannam@226: 	source->page_size   = page_size;
cannam@226: 	source->error_func  = error_func;
cannam@226: 	source->read_func   = read_func;
cannam@226: 
cannam@226: 	if (page_size > 1) {
cannam@226: 		source->file_buf = (uint8_t*)serd_bufalloc(page_size);
cannam@226: 		source->read_buf = source->file_buf;
cannam@226: 		memset(source->file_buf, '\0', page_size);
cannam@226: 	} else {
cannam@226: 		source->read_buf = &source->read_byte;
cannam@226: 	}
cannam@226: 
cannam@226: 	return SERD_SUCCESS;
cannam@226: }
cannam@226: 
cannam@226: SerdStatus
cannam@226: serd_byte_source_prepare(SerdByteSource* source)
cannam@226: {
cannam@226: 	if (!source->prepared) {
cannam@226: 		source->prepared = true;
cannam@226: 		if (source->page_size > 1) {
cannam@226: 			return serd_byte_source_page(source);
cannam@226: 		} else if (source->from_stream) {
cannam@226: 			return serd_byte_source_advance(source);
cannam@226: 		}
cannam@226: 	}
cannam@226: 	return SERD_SUCCESS;
cannam@226: }
cannam@226: 
cannam@226: SerdStatus
cannam@226: serd_byte_source_open_string(SerdByteSource* source, const uint8_t* utf8)
cannam@226: {
cannam@226: 	memset(source, '\0', sizeof(*source));
cannam@226: 	source->read_buf = utf8;
cannam@226: 	source->prepared = true;
cannam@226: 	return SERD_SUCCESS;
cannam@226: }
cannam@226: 
cannam@226: SerdStatus
cannam@226: serd_byte_source_close(SerdByteSource* source)
cannam@226: {
cannam@226: 	if (source->page_size > 1) {
cannam@226: 		free(source->file_buf);
cannam@226: 	}
cannam@226: 	memset(source, '\0', sizeof(*source));
cannam@226: 	return SERD_SUCCESS;
cannam@226: }
cannam@226: 
cannam@226: SerdStatus
cannam@226: serd_byte_source_advance(SerdByteSource* source)
cannam@226: {
cannam@226: 	const bool paging = source->page_size > 1;
cannam@226: 	SerdStatus st     = SERD_SUCCESS;
cannam@226: 	if (source->from_stream && !paging) {
cannam@226: 		if (source->read_func(&source->read_byte, 1, 1, source->stream) == 0) {
cannam@226: 			return (source->error_func(source->stream)
cannam@226: 			        ? SERD_ERR_UNKNOWN : SERD_FAILURE);
cannam@226: 		}
cannam@226: 	} else if (++source->read_head == source->page_size && paging) {
cannam@226: 		st = serd_byte_source_page(source);
cannam@226: 	}
cannam@226: 
cannam@226: 	return st;
cannam@226: }