// -------------------------------------------------------------------- // Basic classes // -------------------------------------------------------------------- /* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2004 Otfried Cheong Ipe is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ipebase.h" #include "ipelet.h" #include #include // -------------------------------------------------------------------- /*! \defgroup base Ipe Base \brief Basic classes for Ipe. Some very basic type definitions, streams, lexical analysis, and XML parsing. All parts of Ipe make use of the STL. The C++ I/O streams library is not used, as Ipelib doesn't do I/O. Ipe objects support internalization and externalization through an abstract interface based on IpeStream's. Clients of Ipelib can use any I/O library that implements this interface. Command line programs can use \c cstdio, Ipe itself uses the Qt file portability layer. */ //! Return the Ipelib version. /*! This is available as a function so that one can verify what version of Ipelib one has actually linked with (as opposed to the header files used during compilation). */ extern int IpelibVersion() { return IPELIB_VERSION; } // -------------------------------------------------------------------- /*! \class IpeString \ingroup base \brief Strings and buffers. IpeString is is an implicitly shared byte string. It is designed to be efficient for strings of arbitrary length, and supposed to be passed by value (the size of IpeString is a single pointer). Sharing is implicit---the string creates its own representation as soon as it is modified. IpeString can be used for binary data. For text, it is usually assumed that the string is UTF-8 encoded, but only the unicode member function actually requires this. In particular, all indices into the string are byte indices, not Unicode character indices. Ipe is not using the STL string type because the C++ standard does neither guarantee that passing by value is efficient, nor that std::string is efficient for very long strings (although in practice that is probably true). However, IpeString is designed to be plug-in compatible with std::string, so Ipe can switch any time it is deemed appropriate (in particular, unlike the rest of Ipe, IpeString uses lower-case member names!). */ IpeString::IpeString() { iImp = new Imp; iImp->iRefCount = 1; iImp->iSize = 0; iImp->iCapacity = 32; iImp->iData = new char[iImp->iCapacity]; } IpeString::IpeString(const char *str) { int len = strlen(str); iImp = new Imp; iImp->iRefCount = 1; iImp->iSize = len; iImp->iCapacity = len + 32; iImp->iData = new char[iImp->iCapacity]; memcpy(iImp->iData, str, iImp->iSize); } IpeString::IpeString(const IpeString &rhs) { iImp = rhs.iImp; iImp->iRefCount++; } IpeString::IpeString(const IpeString &rhs, int index, int len) { // actually available data int len1 = index < rhs.size() ? rhs.size() - index : 0; if (len < 0 || len1 < len) len = len1; iImp = new Imp; iImp->iRefCount = 1; iImp->iSize = len; iImp->iCapacity = len + 32; iImp->iData = new char[iImp->iCapacity]; if (len > 0) memcpy(iImp->iData, rhs.iImp->iData + index, len); } IpeString &IpeString::operator=(const IpeString &rhs) { if (iImp != rhs.iImp) { if (iImp->iRefCount == 1) { delete [] iImp->iData; delete iImp; } else iImp->iRefCount--; iImp = rhs.iImp; iImp->iRefCount++; } return *this; } IpeString::~IpeString() { if (iImp->iRefCount == 1) { delete [] iImp->iData; delete iImp; } else iImp->iRefCount--; } //! Make a private copy of the string with \a n bytes to spare. /*! When a private copy has to be made an extra 32 bytes is ensured. */ void IpeString::Detach(int n) { if (iImp->iRefCount > 1 || (iImp->iSize + n > iImp->iCapacity)) { Imp *imp = new Imp; imp->iRefCount = 1; imp->iSize = iImp->iSize; imp->iCapacity = iImp->iCapacity; while (imp->iSize + 32 + n > imp->iCapacity) imp->iCapacity *= 2; imp->iData = new char[imp->iCapacity]; memcpy(imp->iData, iImp->iData, imp->iSize); iImp->iRefCount--; if (iImp->iRefCount == 0) { delete [] iImp->iData; delete iImp; } iImp = imp; } } const char *IpeString::CString() const { IpeString *This = const_cast(this); if (iImp->iSize == iImp->iCapacity) This->Detach(1); This->iImp->iData[iImp->iSize] = '\0'; return data(); } void IpeString::erase() { Detach(0); iImp->iSize = 0; } void IpeString::append(const IpeString &rhs) { int n = rhs.size(); Detach(n); memcpy(iImp->iData + iImp->iSize, rhs.iImp->iData, n); iImp->iSize += n; } void IpeString::append(char ch) { Detach(1); iImp->iData[iImp->iSize++] = ch; } bool IpeString::operator==(const IpeString &rhs) const { return (size() == rhs.size() && strncmp(iImp->iData, rhs.iImp->iData, size()) == 0); } bool IpeString::operator<(const IpeString &rhs) const { int n = size() < rhs.size() ? size() : rhs.size(); int cmp = strncmp(iImp->iData, rhs.iImp->iData, n); return (cmp < 0 || cmp == 0 && size() < rhs.size()); } IpeString IpeString::operator+(const IpeString &rhs) const { IpeString s(*this); s.append(rhs); return s; } static const uchar bytesFromUTF8[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; static const uchar firstByteMark[7] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0 }; //! Return Unicode value from UTF-8 string. /*! The \a index is incremented to the next UTF-8 character. */ int IpeString::unicode(int &index) const { int wch = uchar(iImp->iData[index++]); if ((wch & 0xc0) == 0x80) { // not on first byte of a UTF-8 sequence while (index < iImp->iSize && (iImp->iData[index] & 0xc0) == 0x80) index++; return 0xfffd; } int extraBytes = bytesFromUTF8[wch & 0xff]; wch -= firstByteMark[extraBytes]; while (extraBytes--) { if (index >= iImp->iSize) return 0xfffd; // UTF-8 sequence is incomplete if ((iImp->iData[index] & 0xc0) != 0x80) return 0xfffd; // UTF-8 sequence is incorrect wch <<= 6; wch |= (iImp->iData[index++] & 0x3f); } return wch; } // -------------------------------------------------------------------- /*! \class IpeFixed \ingroup base \brief Fixed point number with three (decimal) fractional digits */ //! Return value times (a/b) IpeFixed IpeFixed::Mult(int a, int b) const { return IpeFixed::FromInternal(iValue * a / b); } // -------------------------------------------------------------------- /*! \class IpeLex \ingroup base \brief Lexical analyser. Seeded with a string. */ //! Construct lexical analyzer from a string. IpeLex::IpeLex(IpeString str) : iString(str), iPos(0) { // nothing } //! Return NextToken, but without extracting it. IpeString IpeLex::Token() { int pos = iPos; IpeString str = NextToken(); iPos = pos; return str; } /*! Skips any whitespace before the token. Returns empty string if end of string is reached. */ //! Extract next token. IpeString IpeLex::NextToken() { SkipWhitespace(); uint mark = iPos; while (!Eos() && iString[iPos] > ' ') ++iPos; return iString.substr(mark, iPos - mark); } //! Extract integer token (skipping whitespace). int IpeLex::GetInt() { IpeString str = NextToken(); return std::atoi(str.CString()); } inline int HexDigit(int ch) { if ('0' <= ch && ch <= '9') return ch - '0'; if ('a' <= ch && ch <= 'f') return ch - 'a' + 10; if ('A' <= ch && ch <= 'F') return ch - 'A' + 10; return 0; } //! Extract byte in hex (skipping whitespace). int IpeLex::GetHexByte() { int ch1 = '0', ch2 = '0'; SkipWhitespace(); if (!Eos()) ch1 = iString[iPos++]; SkipWhitespace(); if (!Eos()) ch2 = iString[iPos++]; return (HexDigit(ch1) << 4) | HexDigit(ch2); } //! Extract hexadecimal token (skipping whitespace). unsigned long int IpeLex::GetHexNumber() { IpeString str = NextToken(); return std::strtoul(str.CString(), 0, 16); } //! Extract IpeFixed token (skipping whitespace). IpeFixed IpeLex::GetFixed() { IpeString str = NextToken(); int i = 0; while (i < str.size() && str[i] != '.') ++i; int integral = std::strtol(str.substr(0, i).CString(), 0, 10); int fractional = 0; if (i < str.size()) { IpeString s = (str.substr(i+1) + "000").substr(0, 3); fractional = std::strtol(s.CString(), 0, 10); } return IpeFixed::FromInternal(integral * 1000 + fractional); } //! Extract double token (skipping whitespace). double IpeLex::GetDouble() { IpeString str = NextToken(); return std::atof(str.CString()); } //! Skip over whitespace. void IpeLex::SkipWhitespace() { while (!Eos() && iString[iPos] <= ' ') ++iPos; } // -------------------------------------------------------------------- /*! \class IpeBuffer \ingroup base \brief A memory buffer. Implements sharing semantics using reference counting. */ //! Create empty buffer. IpeBuffer::IpeBuffer() { iImp = new Imp; iImp->iRefCount = 1; iImp->iData = 0; iImp->iSize = 0; } //! Create buffer of specified size. IpeBuffer::IpeBuffer(int size) { iImp = new Imp; iImp->iRefCount = 1; iImp->iSize = size; iImp->iData = new char[size]; } //! Copy constructor. Constant time, buffer is shared. IpeBuffer::IpeBuffer(const IpeBuffer &rhs) { iImp = rhs.iImp; iImp->iRefCount++; } //! Create buffer by copying the data. IpeBuffer::IpeBuffer(const char *data, int size) { iImp = new Imp; iImp->iRefCount = 1; iImp->iSize = size; iImp->iData = new char[size]; memcpy(iImp->iData, data, size); } //! Destructor. IpeBuffer::~IpeBuffer() { iImp->iRefCount--; if (iImp->iRefCount == 0) { delete [] iImp->iData; delete iImp; } } //! Assignment operator (constant-time). IpeBuffer &IpeBuffer::operator=(const IpeBuffer &rhs) { if (this != &rhs) { iImp->iRefCount--; if (iImp->iRefCount == 0) { delete [] iImp->iData; delete iImp; } iImp = rhs.iImp; iImp->iRefCount++; } return *this; } // -------------------------------------------------------------------- /*! \class IpeStream \ingroup base \brief Abstract base class for output streams. */ IpeStream::~IpeStream() { // empty implementation of pure virtual destructor } //! Close the stream. No more writing allowed! void IpeStream::Close() { // nothing } //! Default implementation uses PutChar. void IpeStream::PutString(IpeString s) { for (int i = 0; i < s.size(); ++i) PutChar(s[i]); } //! Default implementation uses PutChar. void IpeStream::PutCString(const char *s) { while (*s) PutChar(*s++); } //! Default implementation uses PutChar. void IpeStream::PutRaw(const char *data, int size) { for (int i = 0; i < size; i++) PutChar(data[i]); } //! Output integer. IpeStream &IpeStream::operator<<(int i) { char buf[30]; std::sprintf(buf, "%d", i); *this << buf; return *this; } //! Output double. IpeStream &IpeStream::operator<<(double d) { char buf[30]; std::sprintf(buf, "%g", d); *this << buf; return *this; } //! Output byte in hexadecimal. void IpeStream::PutHexByte(char b) { char buf[3]; std::sprintf(buf, "%02x", (b & 0xff)); *this << buf; } //! Save a string with XML escaping of &, >, <. void IpeStream::PutXmlString(IpeString s) { for (int i = 0; i < s.size(); ++i) { char ch = s[i]; switch (ch) { case '&': *this << "&"; break; case '<': *this << "<"; break; case '>': *this << ">"; break; default: *this << ch; break; } } } // -------------------------------------------------------------------- /*! \class IpeStringStream \ingroup base \brief Stream writing into an IpeString. */ //! Construct with string reference. IpeStringStream::IpeStringStream(IpeString &string) : iString(string) { // nothing } void IpeStringStream::PutChar(char ch) { iString += ch; } void IpeStringStream::PutString(IpeString s) { iString += s; } void IpeStringStream::PutCString(const char *s) { iString += s; } void IpeStringStream::PutRaw(const char *data, int size) { for (int i = 0; i < size; i++) iString += data[i]; } int IpeStringStream::Tell() const { return iString.size(); } // -------------------------------------------------------------------- /*! \class IpeFileStream \ingroup base \brief Stream writing into an open file. */ //! Constructor. IpeFileStream::IpeFileStream(std::FILE *file) : iFile(file) { // nothing } void IpeFileStream::PutChar(char ch) { std::fputc(ch, iFile); } void IpeFileStream::PutString(IpeString s) { for (int i = 0; i < s.size(); ++i) std::fputc(s[i], iFile); } void IpeFileStream::PutCString(const char *s) { fputs(s, iFile); } void IpeFileStream::PutRaw(const char *data, int size) { for (int i = 0; i < size; i++) std::fputc(data[i], iFile); } int IpeFileStream::Tell() const { return std::ftell(iFile); } // -------------------------------------------------------------------- /*! \class IpeDataSource * \ingroup base * \brief Interface for getting data for parsing. */ //! Pure virtual destructor. IpeDataSource::~IpeDataSource() { // nothing } // -------------------------------------------------------------------- /*! \class IpeFileSource \ingroup base \brief Data source for parsing from a file. */ IpeFileSource::IpeFileSource(std::FILE *file) : iFile(file) { // nothing } int IpeFileSource::GetChar() { return std::fgetc(iFile); } /*! \class IpeBufferSource \ingroup base \brief Data source for parsing from a buffer. */ IpeBufferSource::IpeBufferSource(const IpeBuffer &buffer) : iBuffer(buffer) { iPos = 0; } int IpeBufferSource::GetChar() { if (iPos >= iBuffer.size()) return EOF; return uchar(iBuffer[iPos++]); } // -------------------------------------------------------------------- void (*ipeDebugHandler)(const char *) = 0; void ipeDebug(const char *msg, ...) { if (ipeDebugHandler) { char buf[8196]; va_list ap; va_start(ap, msg); std::vsprintf(buf, msg, ap); va_end(ap); ipeDebugHandler(buf); } } // --------------------------------------------------------------------