blob: 712243d70172208f3b4285d1d3f8abc1042028d8 [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "xfa/fgas/crt/ifgas_stream.h"
#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN32_MOBILE_ || \
_FX_OS_ == _FX_WIN64_
#include <io.h>
#endif
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "core/fxcrt/fx_ext.h"
#include "third_party/base/ptr_util.h"
#include "third_party/base/stl_util.h"
#include "xfa/fgas/crt/fgas_codepage.h"
namespace {
class IFGAS_StreamImp {
public:
virtual ~IFGAS_StreamImp() {}
virtual int32_t GetLength() const = 0;
virtual int32_t Seek(FX_STREAMSEEK eSeek, int32_t iOffset) = 0;
virtual int32_t GetPosition() = 0;
virtual bool IsEOF() const = 0;
virtual int32_t ReadData(uint8_t* pBuffer, int32_t iBufferSize) = 0;
virtual int32_t ReadString(wchar_t* pStr, int32_t iMaxLength, bool& bEOS) = 0;
virtual int32_t WriteData(const uint8_t* pBuffer, int32_t iBufferSize) = 0;
virtual int32_t WriteString(const wchar_t* pStr, int32_t iLength) = 0;
virtual void Flush() = 0;
virtual bool SetLength(int32_t iLength) = 0;
protected:
IFGAS_StreamImp();
};
class CFGAS_FileReadStreamImp : public IFGAS_StreamImp {
public:
CFGAS_FileReadStreamImp();
~CFGAS_FileReadStreamImp() override {}
bool LoadFileRead(const CFX_RetainPtr<IFX_SeekableReadStream>& pFileRead);
// IFGAS_StreamImp:
int32_t GetLength() const override;
int32_t Seek(FX_STREAMSEEK eSeek, int32_t iOffset) override;
int32_t GetPosition() override { return m_iPosition; }
bool IsEOF() const override;
int32_t ReadData(uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t ReadString(wchar_t* pStr, int32_t iMaxLength, bool& bEOS) override;
int32_t WriteData(const uint8_t* pBuffer, int32_t iBufferSize) override {
return 0;
}
int32_t WriteString(const wchar_t* pStr, int32_t iLength) override {
return 0;
}
void Flush() override {}
bool SetLength(int32_t iLength) override { return false; }
private:
CFX_RetainPtr<IFX_SeekableReadStream> m_pFileRead;
int32_t m_iPosition;
int32_t m_iLength;
};
class CFGAS_FileWriteStreamImp : public IFGAS_StreamImp {
public:
CFGAS_FileWriteStreamImp();
~CFGAS_FileWriteStreamImp() override {}
bool LoadFileWrite(const CFX_RetainPtr<IFX_SeekableWriteStream>& pFileWrite);
// IFGAS_StreamImp:
int32_t GetLength() const override;
int32_t Seek(FX_STREAMSEEK eSeek, int32_t iOffset) override;
int32_t GetPosition() override { return m_iPosition; }
bool IsEOF() const override;
int32_t ReadData(uint8_t* pBuffer, int32_t iBufferSize) override { return 0; }
int32_t ReadString(wchar_t* pStr, int32_t iMaxLength, bool& bEOS) override {
return 0;
}
int32_t WriteData(const uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t WriteString(const wchar_t* pStr, int32_t iLength) override;
void Flush() override;
bool SetLength(int32_t iLength) override { return false; }
private:
CFX_RetainPtr<IFX_SeekableWriteStream> m_pFileWrite;
int32_t m_iPosition;
};
class CFGAS_TextStream : public IFGAS_Stream {
public:
template <typename T, typename... Args>
friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args);
// IFGAS_Stream
int32_t GetLength() const override;
int32_t Seek(FX_STREAMSEEK eSeek, int32_t iOffset) override;
int32_t GetPosition() override;
bool IsEOF() const override;
int32_t ReadData(uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t ReadString(wchar_t* pStr, int32_t iMaxLength, bool& bEOS) override;
int32_t WriteData(const uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t WriteString(const wchar_t* pStr, int32_t iLength) override;
void Flush() override;
bool SetLength(int32_t iLength) override;
int32_t GetBOMLength() const override;
uint16_t GetCodePage() const override;
uint16_t SetCodePage(uint16_t wCodePage) override;
private:
CFGAS_TextStream(std::unique_ptr<IFGAS_StreamImp> imp, bool isWriteSteam);
~CFGAS_TextStream() override;
void InitStream();
uint16_t m_wCodePage;
int32_t m_wBOMLength;
bool m_IsWriteStream;
std::unique_ptr<IFGAS_StreamImp> m_pStreamImp;
};
class CFGAS_WideStringReadStream : public IFGAS_Stream {
public:
template <typename T, typename... Args>
friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args);
// IFGAS_Stream
int32_t GetLength() const override;
int32_t Seek(FX_STREAMSEEK eSeek, int32_t iOffset) override;
int32_t GetPosition() override;
bool IsEOF() const override;
int32_t ReadData(uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t ReadString(wchar_t* pStr, int32_t iMaxLength, bool& bEOS) override;
int32_t WriteData(const uint8_t* pBuffer, int32_t iBufferSize) override;
int32_t WriteString(const wchar_t* pStr, int32_t iLength) override;
void Flush() override {}
bool SetLength(int32_t iLength) override;
int32_t GetBOMLength() const override;
uint16_t GetCodePage() const override;
uint16_t SetCodePage(uint16_t wCodePage) override;
private:
explicit CFGAS_WideStringReadStream(const CFX_WideString& wsBuffer);
~CFGAS_WideStringReadStream() override;
CFX_WideString m_wsBuffer;
int32_t m_iPosition;
};
IFGAS_StreamImp::IFGAS_StreamImp() {}
CFGAS_FileReadStreamImp::CFGAS_FileReadStreamImp()
: m_pFileRead(nullptr), m_iPosition(0), m_iLength(0) {}
bool CFGAS_FileReadStreamImp::LoadFileRead(
const CFX_RetainPtr<IFX_SeekableReadStream>& pFileRead) {
ASSERT(!m_pFileRead && pFileRead);
m_pFileRead = pFileRead;
m_iLength = m_pFileRead->GetSize();
return true;
}
int32_t CFGAS_FileReadStreamImp::GetLength() const {
return m_iLength;
}
int32_t CFGAS_FileReadStreamImp::Seek(FX_STREAMSEEK eSeek, int32_t iOffset) {
switch (eSeek) {
case FX_STREAMSEEK_Begin:
m_iPosition = iOffset;
break;
case FX_STREAMSEEK_Current:
m_iPosition += iOffset;
break;
case FX_STREAMSEEK_End:
m_iPosition = m_iLength + iOffset;
break;
}
if (m_iPosition < 0) {
m_iPosition = 0;
} else if (m_iPosition >= m_iLength) {
m_iPosition = m_iLength;
}
return m_iPosition;
}
bool CFGAS_FileReadStreamImp::IsEOF() const {
return m_iPosition >= m_iLength;
}
int32_t CFGAS_FileReadStreamImp::ReadData(uint8_t* pBuffer,
int32_t iBufferSize) {
ASSERT(m_pFileRead);
ASSERT(pBuffer && iBufferSize > 0);
if (iBufferSize > m_iLength - m_iPosition) {
iBufferSize = m_iLength - m_iPosition;
}
if (m_pFileRead->ReadBlock(pBuffer, m_iPosition, iBufferSize)) {
m_iPosition += iBufferSize;
return iBufferSize;
}
return 0;
}
int32_t CFGAS_FileReadStreamImp::ReadString(wchar_t* pStr,
int32_t iMaxLength,
bool& bEOS) {
ASSERT(m_pFileRead);
ASSERT(pStr && iMaxLength > 0);
iMaxLength = ReadData((uint8_t*)pStr, iMaxLength * 2) / 2;
if (iMaxLength <= 0) {
return 0;
}
int32_t i = 0;
while (i < iMaxLength && pStr[i] != L'\0') {
++i;
}
bEOS = (m_iPosition >= m_iLength) || pStr[i] == L'\0';
return i;
}
CFGAS_FileWriteStreamImp::CFGAS_FileWriteStreamImp()
: m_pFileWrite(nullptr), m_iPosition(0) {}
bool CFGAS_FileWriteStreamImp::LoadFileWrite(
const CFX_RetainPtr<IFX_SeekableWriteStream>& pFileWrite) {
ASSERT(!m_pFileWrite && pFileWrite);
m_iPosition = pFileWrite->GetSize();
m_pFileWrite = pFileWrite;
return true;
}
int32_t CFGAS_FileWriteStreamImp::GetLength() const {
if (!m_pFileWrite)
return 0;
return (int32_t)m_pFileWrite->GetSize();
}
int32_t CFGAS_FileWriteStreamImp::Seek(FX_STREAMSEEK eSeek, int32_t iOffset) {
int32_t iLength = GetLength();
switch (eSeek) {
case FX_STREAMSEEK_Begin:
m_iPosition = iOffset;
break;
case FX_STREAMSEEK_Current:
m_iPosition += iOffset;
break;
case FX_STREAMSEEK_End:
m_iPosition = iLength + iOffset;
break;
}
if (m_iPosition < 0) {
m_iPosition = 0;
} else if (m_iPosition >= iLength) {
m_iPosition = iLength;
}
return m_iPosition;
}
bool CFGAS_FileWriteStreamImp::IsEOF() const {
return m_iPosition >= GetLength();
}
int32_t CFGAS_FileWriteStreamImp::WriteData(const uint8_t* pBuffer,
int32_t iBufferSize) {
if (!m_pFileWrite) {
return 0;
}
if (m_pFileWrite->WriteBlock(pBuffer, m_iPosition, iBufferSize)) {
m_iPosition += iBufferSize;
}
return iBufferSize;
}
int32_t CFGAS_FileWriteStreamImp::WriteString(const wchar_t* pStr,
int32_t iLength) {
return WriteData((const uint8_t*)pStr, iLength * sizeof(wchar_t));
}
void CFGAS_FileWriteStreamImp::Flush() {
if (m_pFileWrite) {
m_pFileWrite->Flush();
}
}
CFGAS_TextStream::CFGAS_TextStream(std::unique_ptr<IFGAS_StreamImp> imp,
bool isWriteStream)
: m_wCodePage(FX_CODEPAGE_DefANSI),
m_wBOMLength(0),
m_IsWriteStream(isWriteStream),
m_pStreamImp(std::move(imp)) {
ASSERT(m_pStreamImp);
InitStream();
}
CFGAS_TextStream::~CFGAS_TextStream() {}
#if _FX_ENDIAN_ == _FX_LITTLE_ENDIAN_
#define BOM_MASK 0x00FFFFFF
#define BOM_UTF8 0x00BFBBEF
#define BOM_UTF16_MASK 0x0000FFFF
#define BOM_UTF16_BE 0x0000FFFE
#define BOM_UTF16_LE 0x0000FEFF
#else
#define BOM_MASK 0xFFFFFF00
#define BOM_UTF8 0xEFBBBF00
#define BOM_UTF16_MASK 0xFFFF0000
#define BOM_UTF16_BE 0xFEFF0000
#define BOM_UTF16_LE 0xFFFE0000
#endif // _FX_ENDIAN_ == _FX_LITTLE_ENDIAN_
void CFGAS_TextStream::InitStream() {
int32_t iPosition = m_pStreamImp->GetPosition();
m_pStreamImp->Seek(FX_STREAMSEEK_Begin, 0);
uint32_t bom;
m_pStreamImp->ReadData(reinterpret_cast<uint8_t*>(&bom), 3);
bom &= BOM_MASK;
if (bom == BOM_UTF8) {
m_wBOMLength = 3;
m_wCodePage = FX_CODEPAGE_UTF8;
} else {
bom &= BOM_UTF16_MASK;
if (bom == BOM_UTF16_BE) {
m_wBOMLength = 2;
m_wCodePage = FX_CODEPAGE_UTF16BE;
} else if (bom == BOM_UTF16_LE) {
m_wBOMLength = 2;
m_wCodePage = FX_CODEPAGE_UTF16LE;
} else {
m_wBOMLength = 0;
m_wCodePage = FXSYS_GetACP();
}
}
m_pStreamImp->Seek(FX_STREAMSEEK_Begin, std::max(m_wBOMLength, iPosition));
}
int32_t CFGAS_TextStream::GetLength() const {
return m_pStreamImp->GetLength();
}
int32_t CFGAS_TextStream::Seek(FX_STREAMSEEK eSeek, int32_t iOffset) {
return m_pStreamImp->Seek(eSeek, iOffset);
}
int32_t CFGAS_TextStream::GetPosition() {
return m_pStreamImp->GetPosition();
}
bool CFGAS_TextStream::IsEOF() const {
return m_pStreamImp->IsEOF();
}
int32_t CFGAS_TextStream::ReadData(uint8_t* pBuffer, int32_t iBufferSize) {
ASSERT(pBuffer && iBufferSize > 0);
if (m_IsWriteStream)
return -1;
int32_t iLen = std::min(
m_pStreamImp->GetLength() - m_pStreamImp->GetPosition(), iBufferSize);
if (iLen <= 0)
return 0;
return m_pStreamImp->ReadData(pBuffer, iLen);
}
int32_t CFGAS_TextStream::WriteData(const uint8_t* pBuffer,
int32_t iBufferSize) {
ASSERT(pBuffer && iBufferSize > 0);
if (!m_IsWriteStream)
return -1;
return m_pStreamImp->WriteData(pBuffer, iBufferSize);
}
void CFGAS_TextStream::Flush() {
if (!m_IsWriteStream)
return;
m_pStreamImp->Flush();
}
bool CFGAS_TextStream::SetLength(int32_t iLength) {
if (!m_IsWriteStream)
return false;
return m_pStreamImp->SetLength(iLength);
}
int32_t CFGAS_TextStream::GetBOMLength() const {
if (m_wBOMLength < 1)
return 0;
return m_wBOMLength;
}
uint16_t CFGAS_TextStream::GetCodePage() const {
return m_wCodePage;
}
uint16_t CFGAS_TextStream::SetCodePage(uint16_t wCodePage) {
if (m_wBOMLength > 0)
return m_wCodePage;
uint16_t v = m_wCodePage;
m_wCodePage = wCodePage;
return v;
}
int32_t CFGAS_TextStream::ReadString(wchar_t* pStr,
int32_t iMaxLength,
bool& bEOS) {
ASSERT(pStr && iMaxLength > 0);
if (m_IsWriteStream)
return -1;
if (m_wCodePage == FX_CODEPAGE_UTF16LE ||
m_wCodePage == FX_CODEPAGE_UTF16BE) {
int32_t iBytes = iMaxLength * 2;
int32_t iLen = m_pStreamImp->ReadData((uint8_t*)pStr, iBytes);
iMaxLength = iLen / 2;
if (sizeof(wchar_t) > 2)
FX_UTF16ToWChar(pStr, iMaxLength);
#if _FX_ENDIAN_ == _FX_BIG_ENDIAN_
if (m_wCodePage == FX_CODEPAGE_UTF16LE)
FX_SwapByteOrder(pStr, iMaxLength);
#else
if (m_wCodePage == FX_CODEPAGE_UTF16BE)
FX_SwapByteOrder(pStr, iMaxLength);
#endif
} else {
int32_t pos = m_pStreamImp->GetPosition();
int32_t iBytes = std::min(iMaxLength, m_pStreamImp->GetLength() - pos);
if (iBytes > 0) {
std::vector<uint8_t> buf(iBytes);
int32_t iLen = m_pStreamImp->ReadData(buf.data(), iBytes);
int32_t iSrc = iLen;
int32_t iDecode = FX_DecodeString(
m_wCodePage, reinterpret_cast<const char*>(buf.data()), &iSrc, pStr,
&iMaxLength, true);
m_pStreamImp->Seek(FX_STREAMSEEK_Current, iSrc - iLen);
if (iDecode < 1)
return -1;
} else {
iMaxLength = 0;
}
}
bEOS = m_pStreamImp->IsEOF();
return iMaxLength;
}
int32_t CFGAS_TextStream::WriteString(const wchar_t* pStr, int32_t iLength) {
ASSERT(pStr && iLength > 0);
if (!m_IsWriteStream)
return -1;
if (m_wCodePage == FX_CODEPAGE_UTF8) {
int32_t len = iLength;
CFX_UTF8Encoder encoder;
while (len-- > 0) {
encoder.Input(*pStr++);
}
CFX_ByteStringC bsResult = encoder.GetResult();
m_pStreamImp->WriteData((const uint8_t*)bsResult.c_str(),
bsResult.GetLength());
}
return iLength;
}
CFGAS_WideStringReadStream::CFGAS_WideStringReadStream(
const CFX_WideString& wsBuffer)
: m_wsBuffer(wsBuffer), m_iPosition(0) {}
CFGAS_WideStringReadStream::~CFGAS_WideStringReadStream() {}
int32_t CFGAS_WideStringReadStream::GetLength() const {
return m_wsBuffer.GetLength() * sizeof(wchar_t);
}
int32_t CFGAS_WideStringReadStream::Seek(FX_STREAMSEEK eSeek, int32_t iOffset) {
switch (eSeek) {
case FX_STREAMSEEK_Begin:
m_iPosition = iOffset;
break;
case FX_STREAMSEEK_Current:
m_iPosition += iOffset;
break;
case FX_STREAMSEEK_End:
m_iPosition = m_wsBuffer.GetLength() + iOffset;
break;
}
m_iPosition = pdfium::clamp(0, m_iPosition, m_wsBuffer.GetLength());
return GetPosition();
}
int32_t CFGAS_WideStringReadStream::GetPosition() {
return m_iPosition * sizeof(wchar_t);
}
bool CFGAS_WideStringReadStream::IsEOF() const {
return m_iPosition >= m_wsBuffer.GetLength();
}
int32_t CFGAS_WideStringReadStream::ReadData(uint8_t* pBuffer,
int32_t iBufferSize) {
return 0;
}
int32_t CFGAS_WideStringReadStream::ReadString(wchar_t* pStr,
int32_t iMaxLength,
bool& bEOS) {
iMaxLength = std::min(iMaxLength, m_wsBuffer.GetLength() - m_iPosition);
if (iMaxLength == 0)
return 0;
FXSYS_wcsncpy(pStr, m_wsBuffer.c_str() + m_iPosition, iMaxLength);
m_iPosition += iMaxLength;
bEOS = IsEOF();
return iMaxLength;
}
int32_t CFGAS_WideStringReadStream::WriteData(const uint8_t* pBuffer,
int32_t iBufferSize) {
return 0;
}
int32_t CFGAS_WideStringReadStream::WriteString(const wchar_t* pStr,
int32_t iLength) {
return 0;
}
bool CFGAS_WideStringReadStream::SetLength(int32_t iLength) {
return false;
}
int32_t CFGAS_WideStringReadStream::GetBOMLength() const {
return 0;
}
uint16_t CFGAS_WideStringReadStream::GetCodePage() const {
return (sizeof(wchar_t) == 2) ? FX_CODEPAGE_UTF16LE : FX_CODEPAGE_UTF32LE;
}
uint16_t CFGAS_WideStringReadStream::SetCodePage(uint16_t wCodePage) {
return GetCodePage();
}
} // namespace
// static
CFX_RetainPtr<IFGAS_Stream> IFGAS_Stream::CreateReadStream(
const CFX_RetainPtr<IFX_SeekableReadStream>& pFileRead) {
if (!pFileRead)
return nullptr;
std::unique_ptr<IFGAS_StreamImp> pImp =
pdfium::MakeUnique<CFGAS_FileReadStreamImp>();
if (!static_cast<CFGAS_FileReadStreamImp*>(pImp.get())
->LoadFileRead(pFileRead)) {
return nullptr;
}
return pdfium::MakeRetain<CFGAS_TextStream>(std::move(pImp), false);
}
// static
CFX_RetainPtr<IFGAS_Stream> IFGAS_Stream::CreateWriteStream(
const CFX_RetainPtr<IFX_SeekableWriteStream>& pFileWrite) {
if (!pFileWrite)
return nullptr;
std::unique_ptr<IFGAS_StreamImp> pImp =
pdfium::MakeUnique<CFGAS_FileWriteStreamImp>();
if (!static_cast<CFGAS_FileWriteStreamImp*>(pImp.get())
->LoadFileWrite(pFileWrite)) {
return nullptr;
}
return pdfium::MakeRetain<CFGAS_TextStream>(std::move(pImp), true);
}
// static
CFX_RetainPtr<IFGAS_Stream> IFGAS_Stream::CreateWideStringReadStream(
const CFX_WideString& buffer) {
return pdfium::MakeRetain<CFGAS_WideStringReadStream>(buffer);
}