#include "KinoSearch/Util/ToolSet.h"
#define KINO_WANT_OUTSTREAM_VTABLE
#include "KinoSearch/Store/OutStream.r"
#include "KinoSearch/Store/FileDes.r"
#include "KinoSearch/Store/InStream.r"
OutStream*
OutStream_new(FileDes *file_des)
{
CREATE(self, OutStream, OUTSTREAM);
/* init */
self->buf = MALLOCATE(KINO_IO_STREAM_BUF_SIZE, char);
self->buf_start = 0;
self->buf_pos = 0;
self->is_closed = false;
/* assign */
self->file_des = REFCOUNT_INC(file_des);
/* increment */
file_des->stream_count++;
return self;
}
void
OutStream_destroy(OutStream *self)
{
if (!self->is_closed)
OutStream_SFlush(self);
REFCOUNT_DEC(self->file_des);
free(self->buf);
free(self);
}
void
OutStream_absorb(OutStream *self, InStream *instream)
{
char buf[KINO_IO_STREAM_BUF_SIZE];
u64_t bytes_left = InStream_SLength(instream);
while (bytes_left) {
const u32_t bytes_this_iter = bytes_left < IO_STREAM_BUF_SIZE
? bytes_left
: IO_STREAM_BUF_SIZE;
InStream_Read_Bytes(instream, buf, bytes_this_iter);
OutStream_write_bytes(self, buf, bytes_this_iter);
bytes_left -= bytes_this_iter;
}
}
u64_t
OutStream_stell(OutStream *self)
{
return self->buf_start + self->buf_pos;
}
void
OutStream_sflush(OutStream *self)
{
if ( !FileDes_FDWrite(self->file_des, self->buf, self->buf_pos) ) {
CONFESS("Error: %s", Carp_kerror);
}
self->buf_start += self->buf_pos;
self->buf_pos = 0;
}
u64_t
OutStream_slength(OutStream *self)
{
OutStream_SFlush(self);
return FileDes_FDLength(self->file_des);
}
void
OutStream_write_byte(OutStream *self, char aChar)
{
if (self->buf_pos >= KINO_IO_STREAM_BUF_SIZE)
OutStream_SFlush(self);
self->buf[ self->buf_pos++ ] = aChar;
}
void
OutStream_write_bytes(OutStream *self, const char *bytes, size_t len)
{
/* if this data is larger than the buffer size, flush and write */
if (len >= KINO_IO_STREAM_BUF_SIZE) {
OutStream_SFlush(self);
if ( !FileDes_FDWrite(self->file_des, bytes, len) )
CONFESS("Error: %s", Carp_kerror);
self->buf_start += len;
}
/* if there's not enough room in the buffer, flush then add */
else if (self->buf_pos + len >= KINO_IO_STREAM_BUF_SIZE) {
OutStream_SFlush(self);
memcpy((self->buf + self->buf_pos), bytes,
len * sizeof(char));
self->buf_pos += len;
}
/* if there's room, just add these bytes to the buffer */
else {
memcpy((self->buf + self->buf_pos), bytes,
len * sizeof(char));
self->buf_pos += len;
}
}
void
OutStream_write_int(OutStream *self, u32_t aU32)
{
#ifdef BIG_END
OutStream_Write_Bytes(self, (char*)&aU32, 4);
#else
u8_t buf[4];
MATH_ENCODE_U32(aU32, buf);
OutStream_Write_Bytes(self, (char*)buf, 4);
#endif
}
void
OutStream_write_long(OutStream *self, u64_t aQuad)
{
u8_t buf[8];
/* store as big-endian */
buf[0] = (aQuad >> 56) & 0xFF;
buf[1] = (aQuad >> 48) & 0xFF;
buf[2] = (aQuad >> 40) & 0xFF;
buf[3] = (aQuad >> 32) & 0xFF;
buf[4] = (aQuad >> 24) & 0xFF;
buf[5] = (aQuad >> 16) & 0xFF;
buf[6] = (aQuad >> 8 ) & 0xFF;
buf[7] = (aQuad ) & 0xFF;
/* print encoded Long to the output handle */
OutStream_Write_Bytes(self, (char*)buf, 8);
}
void
OutStream_write_vint(OutStream *self, u32_t aU32)
{
u8_t buf[VINT_MAX_BYTES];
u8_t *ptr = buf + sizeof(buf) - 1;
/* write last byte first, which has no continue bit */
*ptr = aU32 & 0x7f;
aU32 >>= 7;
while (aU32) {
/* work backwards, writing bytes with continue bits set */
*--ptr = ((aU32 & 0x7f) | 0x80);
aU32 >>= 7;
}
OutStream_write_bytes(self, (char*)ptr, (buf + sizeof(buf)) - ptr);
}
void
OutStream_write_vlong(OutStream *self, u64_t aQuad)
{
u8_t buf[VLONG_MAX_BYTES];
u8_t *ptr = buf + sizeof(buf) - 1;
/* write last byte first, which has no continue bit */
*ptr = aQuad & 0x7f;
aQuad >>= 7;
while (aQuad) {
/* work backwards, writing bytes with continue bits set */
*--ptr = ((aQuad & 0x7f) | 0x80);
aQuad >>= 7;
}
OutStream_write_bytes(self, (char*)ptr, (buf + sizeof(buf)) - ptr);
}
void
OutStream_write_string(OutStream *self, const char *string, size_t len)
{
OutStream_write_vint(self, (u32_t)len);
OutStream_write_bytes(self, string, len);
}
void
OutStream_sclose(OutStream *self)
{
OutStream_SFlush(self);
if (--self->file_des->stream_count <= 0) {
if ( !FileDes_FDClose(self->file_des) ) {
CONFESS("Error closing '%s': %s", self->file_des->path,
Carp_kerror);
}
}
self->is_closed = true;
}
/* Copyright 2006-2007 Marvin Humphrey
*
* This program is free software; you can redistribute it and/or modify
* under the same terms as Perl itself.
*/