mxwcore-legion/dep/g3dlite/include/G3D/BinaryInput.h

459 lines
13 KiB
C
Raw Normal View History

2023-11-05 15:26:19 -05:00
/**
\file G3D/BinaryInput.h
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
\created 2001-08-09
\edited 2013-01-03
Copyright 2000-2012, Morgan McGuire.
All rights reserved.
*/
#ifndef G3D_BinaryInput_h
#define G3D_BinaryInput_h
#ifdef _MSC_VER
// Disable conditional expression is constant, which occurs incorrectly on inlined functions
# pragma warning(push)
# pragma warning( disable : 4127 )
#endif
#include <assert.h>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include "G3D/platform.h"
#include "G3D/unorm8.h"
#include "G3D/Array.h"
#include "G3D/Color4.h"
#include "G3D/Color3.h"
#include "G3D/Vector4.h"
#include "G3D/Vector3.h"
#include "G3D/Vector2.h"
#include "G3D/g3dmath.h"
#include "G3D/debug.h"
#include "G3D/System.h"
namespace G3D {
#if defined(G3D_WINDOWS) || defined(G3D_LINUX)
// Allow writing of integers to non-word aligned locations.
// This is legal on x86, but not on other platforms.
#define G3D_ALLOW_UNALIGNED_WRITES
#endif
/**
Sequential or random access byte-order independent binary file access.
Files compressed with zlib and beginning with an unsigned 32-bit int
size are transparently decompressed when the compressed = true flag is
specified to the constructor.
For every readX method there are also versions that operate on a whole
Array, std::vector, or C-array. e.g. readFloat32(Array<float32>& array, n)
These methods resize the array or std::vector to the appropriate size
before reading. For a C-array, they require the pointer to reference
a memory block at least large enough to hold <I>n</I> elements.
Most classes define serialize/deserialize methods that use BinaryInput,
BinaryOutput, TextInput, and TextOutput. There are text serializer
functions for primitive types (e.g. int, std::string, float, double) but not
binary serializers-- you <B>must</b> call the BinaryInput::readInt32 or
other appropriate function. This is because it would be very hard to
debug the error sequence: <CODE>serialize(1.0, bo); ... float f; deserialize(f, bi);</CODE>
in which a double is serialized and then deserialized as a float.
*/
class BinaryInput {
private:
// The initial buffer will be no larger than this, but
// may grow if a large memory read occurs. 750 MB
static const int64
INITIAL_BUFFER_LENGTH =
#ifdef G3D_64BIT
5000000000L // 5 GB
#else
750000000 // 750 MB
#endif
;
/**
is the file big or little endian
*/
G3DEndian m_fileEndian;
std::string m_filename;
bool m_swapBytes;
/** Next position to read from in bitString during readBits. */
int m_bitPos;
/** Bits currently being read by readBits.
Contains at most 8 (low) bits. Note that
beginBits/readBits actually consumes one extra byte, which
will be restored by writeBits.*/
uint32 m_bitString;
/** 1 when between beginBits and endBits, 0 otherwise. */
int m_beginEndBits;
/** When operating on huge files, we cannot load the whole file into memory.
This is the file position to which buffer[0] corresponds.
Even 32-bit code can load 64-bit files in chunks, so this is not size_t
*/
int64 m_alreadyRead;
/**
Length of the entire file, in bytes.
For the length of the buffer, see bufferLength
*/
int64 m_length;
/** Length of the array referenced by buffer. May go past the end of the file!*/
int64 m_bufferLength;
uint8* m_buffer;
/**
Next byte in file, relative to buffer.
*/
int64 m_pos;
/**
When true, the buffer is freed in the destructor.
*/
bool m_freeBuffer;
/** Ensures that we are able to read at least minLength from startPosition (relative
to start of file). */
void loadIntoMemory(int64 startPosition, int64 minLength = 0);
/** Verifies that at least this number of bytes can be read.*/
void prepareToRead(int64 nbytes);
// Not implemented on purpose, don't use
BinaryInput(const BinaryInput&);
BinaryInput& operator=(const BinaryInput&);
bool operator==(const BinaryInput&);
/** Buffer is compressed; replace it with a decompressed version */
void decompress();
public:
/** false, constant to use with the copyMemory option */
static const bool NO_COPY;
/**
If the file cannot be opened, a zero length buffer is presented.
Automatically opens files that are inside zipfiles.
@param compressed Set to true if and only if the file was
compressed using BinaryOutput's zlib compression. This has
nothing to do with whether the input is in a zipfile.
*/
BinaryInput(
const std::string& filename,
G3DEndian fileEndian,
bool compressed = false);
/**
Creates input stream from an in memory source.
Unless you specify copyMemory = false, the data is copied
from the pointer, so you may deallocate it as soon as the
object is constructed. It is an error to specify copyMemory = false
and compressed = true.
To decompress part of a file, you can follow the following paradigm:
\htmlonly
<PRE>
BinaryInput master(...);
// read from master to point where compressed data exists.
BinaryInput subset(master.getCArray() + master.getPosition(),
master.length() - master.getPosition(),
master.endian(), true, true);
// Now read from subset (it is ok for master to go out of scope)
</PRE>
\endhtmlonly
*/
BinaryInput(
const uint8* data,
int64 dataLen,
G3DEndian dataEndian,
bool compressed = false,
bool copyMemory = true);
virtual ~BinaryInput();
/** Change the endian-ness of the file. This only changes the
interpretation of the file for future read calls; the
underlying data is unmodified.*/
void setEndian(G3DEndian endian);
G3DEndian endian() const {
return m_fileEndian;
}
std::string getFilename() const {
return m_filename;
}
/**
Performs bounds checks in debug mode. [] are relative to
the start of the file, not the current position.
Seeks to the new position before reading (and leaves
that as the current position)
*/
uint8 operator[](int64 n) {
setPosition(n);
return readUInt8();
}
/**
Returns the length of the file in bytes.
*/
int64 getLength() const {
return m_length;
}
int64 size() const {
return getLength();
}
/**
Returns the current byte position in the file,
where 0 is the beginning and getLength() - 1 is the end.
*/
int64 getPosition() const {
return m_pos + m_alreadyRead;
}
/**
Returns a pointer to the internal memory buffer.
May throw an exception for huge files.
*/
const uint8* getCArray() {
if (m_alreadyRead > 0 || m_bufferLength < m_length) {
throw "Cannot getCArray for a huge file";
}
return m_buffer;
}
/**
Sets the position. Cannot set past length.
May throw a char* when seeking backwards more than 10 MB on a huge file.
*/
void setPosition(int64 p) {
debugAssertM(p <= m_length, "Read past end of file");
m_pos = p - m_alreadyRead;
if ((m_pos < 0) || (m_pos > m_bufferLength)) {
loadIntoMemory(m_pos + m_alreadyRead);
}
}
/**
Goes back to the beginning of the file.
*/
void reset() {
setPosition(0);
}
void readBytes(void* bytes, int64 n);
int8 readInt8() {
prepareToRead(1);
return m_buffer[m_pos++];
}
bool readBool8() {
return (readInt8() != 0);
}
uint8 readUInt8() {
prepareToRead(1);
return ((uint8*)m_buffer)[m_pos++];
}
unorm8 readUNorm8() {
return unorm8::fromBits(readUInt8());
}
uint16 readUInt16() {
prepareToRead(2);
m_pos += 2;
if (m_swapBytes) {
uint8 out[2];
out[0] = m_buffer[m_pos - 1];
out[1] = m_buffer[m_pos - 2];
return *(uint16*)out;
} else {
#ifdef G3D_ALLOW_UNALIGNED_WRITES
return *(uint16*)(&m_buffer[m_pos - 2]);
#else
uint8 out[2];
out[0] = m_buffer[m_pos - 2];
out[1] = m_buffer[m_pos - 1];
return *(uint16*)out;
#endif
}
}
int16 readInt16() {
uint16 a = readUInt16();
return *(int16*)&a;
}
uint32 readUInt32() {
prepareToRead(4);
m_pos += 4;
if (m_swapBytes) {
uint8 out[4];
out[0] = m_buffer[m_pos - 1];
out[1] = m_buffer[m_pos - 2];
out[2] = m_buffer[m_pos - 3];
out[3] = m_buffer[m_pos - 4];
return *(uint32*)out;
} else {
#ifdef G3D_ALLOW_UNALIGNED_WRITES
return *(uint32*)(&m_buffer[m_pos - 4]);
#else
uint8 out[4];
out[0] = m_buffer[m_pos - 4];
out[1] = m_buffer[m_pos - 3];
out[2] = m_buffer[m_pos - 2];
out[3] = m_buffer[m_pos - 1];
return *(uint32*)out;
#endif
}
}
int32 readInt32() {
uint32 a = readUInt32();
return *(int32*)&a;
}
uint64 readUInt64();
int64 readInt64() {
uint64 a = readUInt64();
return *(int64*)&a;
}
float32 readFloat32() {
union {
uint32 a;
float32 b;
};
a = readUInt32();
return b;
}
float64 readFloat64() {
union {
uint64 a;
float64 b;
};
a = readUInt64();
return b;
}
/**
Always consumes \a maxLength characters. Reads a string until NULL or \a maxLength characters. Does not require NULL termination.
*/
std::string readString(int64 maxLength);
/**
Reads a string until NULL or end of file.
*/
std::string readString();
/** Read a string (which may contain NULLs) of exactly numBytes bytes, including the final terminator if there is one. If there is a NULL in the string before
the end, then only the part up to the first NULL is returned although all bytes are read.*/
std::string readFixedLengthString(int numBytes);
/**
Reads a string until NULL, newline ("&#92;r", "&#92;n", "&#92;r&#92;n", "&#92;n&#92;r") or the end of the file is encountered. Consumes the newline.
*/
std::string readStringNewline();
/**
Reads until NULL or the end of the file is encountered.
If the string has odd length (including NULL), reads
another byte. This is a common format for 16-bit alignment
in files.
*/
std::string readStringEven();
/** Reads a uint32 and then calls readString(maxLength) with that value as the length. */
std::string readString32();
Vector4 readVector4();
Vector3 readVector3();
Vector2 readVector2();
Color4 readColor4();
Color3 readColor3();
/**
Skips ahead n bytes.
*/
void skip(int64 n) {
setPosition(m_pos + m_alreadyRead + n);
}
/**
Returns true if the position is not at the end of the file
*/
bool hasMore() const {
return m_pos + m_alreadyRead < m_length;
}
/** Prepares for bit reading via readBits. Only readBits can be
called between beginBits and endBits without corrupting the
data stream. */
void beginBits();
/** Can only be called between beginBits and endBits */
uint32 readBits(int numBits);
/** Ends bit-reading. */
void endBits();
# define DECLARE_READER(ucase, lcase)\
void read##ucase(lcase* out, int64 n);\
void read##ucase(std::vector<lcase>& out, int64 n);\
void read##ucase(Array<lcase>& out, int64 n);
DECLARE_READER(Bool8, bool)
DECLARE_READER(UInt8, uint8)
DECLARE_READER(Int8, int8)
DECLARE_READER(UInt16, uint16)
DECLARE_READER(Int16, int16)
DECLARE_READER(UInt32, uint32)
DECLARE_READER(Int32, int32)
DECLARE_READER(UInt64, uint64)
DECLARE_READER(Int64, int64)
DECLARE_READER(Float32, float32)
DECLARE_READER(Float64, float64)
# undef DECLARE_READER
};
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif