Quelle unoidlprovider.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
#include <sal/config.h>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <set>
#include <string_view>
#include <utility>
#include <vector>
#include <o3tl/string_view.hxx>
#include <osl/endian.h>
#include <osl/file.h>
#include <rtl/character.hxx>
#include <rtl/ref.hxx>
#include <rtl/textenc.h>
#include <rtl/textcvt.h>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
#include <salhelper/simplereferenceobject.hxx>
#include <unoidl/unoidl.hxx>
#include "unoidlprovider.hxx"
namespace unoidl::detail {
class MappedFile: public salhelper::SimpleReferenceObject {
public :
explicit MappedFile(OUString fileUrl);
sal_uInt8 read8(sal_uInt32 offset) const ;
sal_uInt16 read16(sal_uInt32 offset) const ;
sal_uInt32 read32(sal_uInt32 offset) const ;
sal_uInt64 read64(sal_uInt32 offset) const ;
float readIso60599Binary32(sal_uInt32 offset) const ;
double readIso60599Binary64(sal_uInt32 offset) const ;
OUString readNulName(sal_uInt32 offset) /*const*/;
OUString readIdxName(sal_uInt32 * offset) const
{ return readIdxString(offset, RTL_TEXTENCODING_ASCII_US); }
OUString readIdxString(sal_uInt32 * offset) const
{ return readIdxString(offset, RTL_TEXTENCODING_UTF8); }
OUString uri;
oslFileHandle handle;
sal_uInt64 size;
void * address;
private :
virtual ~MappedFile() override;
sal_uInt8 get8(sal_uInt32 offset) const ;
sal_uInt16 get16(sal_uInt32 offset) const ;
sal_uInt32 get32(sal_uInt32 offset) const ;
sal_uInt64 get64(sal_uInt32 offset) const ;
float getIso60599Binary32(sal_uInt32 offset) const ;
double getIso60599Binary64(sal_uInt32 offset) const ;
OUString readIdxString(sal_uInt32 * offset, rtl_TextEncoding encoding)
const ;
};
namespace {
// sizeof (Memory16) == 2
struct Memory16 {
unsigned char byte[2 ];
sal_uInt16 getUnsigned16() const {
return static_cast < sal_uInt16 >(byte[0 ])
| (static_cast < sal_uInt16 >(byte[1 ]) << 8 );
}
};
// sizeof (Memory32) == 4
struct Memory32 {
unsigned char byte[4 ];
sal_uInt32 getUnsigned32() const {
return static_cast < sal_uInt32 >(byte[0 ])
| (static_cast < sal_uInt32 >(byte[1 ]) << 8 )
| (static_cast < sal_uInt32 >(byte[2 ]) << 16 )
| (static_cast < sal_uInt32 >(byte[3 ]) << 24 );
}
float getIso60599Binary32() const {
union {
unsigned char buf[4 ];
float f; // assuming float is ISO 60599 binary32
} sa;
#if defined OSL_LITENDIAN
sa.buf[0 ] = byte[0 ];
sa.buf[1 ] = byte[1 ];
sa.buf[2 ] = byte[2 ];
sa.buf[3 ] = byte[3 ];
#else
sa.buf[0 ] = byte[3 ];
sa.buf[1 ] = byte[2 ];
sa.buf[2 ] = byte[1 ];
sa.buf[3 ] = byte[0 ];
#endif
return sa.f;
}
};
// sizeof (Memory64) == 8
struct Memory64 {
unsigned char byte[8 ];
sal_uInt64 getUnsigned64() const {
return static_cast < sal_uInt64 >(byte[0 ])
| (static_cast < sal_uInt64 >(byte[1 ]) << 8 )
| (static_cast < sal_uInt64 >(byte[2 ]) << 16 )
| (static_cast < sal_uInt64 >(byte[3 ]) << 24 )
| (static_cast < sal_uInt64 >(byte[4 ]) << 32 )
| (static_cast < sal_uInt64 >(byte[5 ]) << 40 )
| (static_cast < sal_uInt64 >(byte[6 ]) << 48 )
| (static_cast < sal_uInt64 >(byte[7 ]) << 56 );
}
double getIso60599Binary64() const {
union {
unsigned char buf[8 ];
double d; // assuming double is ISO 60599 binary64
} sa;
#if defined OSL_LITENDIAN
sa.buf[0 ] = byte[0 ];
sa.buf[1 ] = byte[1 ];
sa.buf[2 ] = byte[2 ];
sa.buf[3 ] = byte[3 ];
sa.buf[4 ] = byte[4 ];
sa.buf[5 ] = byte[5 ];
sa.buf[6 ] = byte[6 ];
sa.buf[7 ] = byte[7 ];
#else
sa.buf[0 ] = byte[7 ];
sa.buf[1 ] = byte[6 ];
sa.buf[2 ] = byte[5 ];
sa.buf[3 ] = byte[4 ];
sa.buf[4 ] = byte[3 ];
sa.buf[5 ] = byte[2 ];
sa.buf[6 ] = byte[1 ];
sa.buf[7 ] = byte[0 ];
#endif
return sa.d;
}
};
bool isSimpleType(std::u16string_view type) {
return type == u"void" || type == u"boolean" || type == u"byte"
|| type == u"short" || type == u"unsigned short" || type == u"long"
|| type == u"unsigned long" || type == u"hyper"
|| type == u"unsigned hyper" || type == u"float" || type == u"double"
|| type == u"char" || type == u"string" || type == u"type"
|| type == u"any" ;
}
// For backwards compatibility, does not strictly check segments to match
//
// <segment> ::= <blocks> | <block>
// <blocks> ::= <capital> <other>* ("_" <block>)*
// <block> ::= <other>+
// <other> ::= <capital> | "a"--"z" | "0"--"9"
// <capital> ::= "A"--"Z"
//
bool isIdentifier(std::u16string_view type, bool scoped) {
if (type.empty()) {
return false ;
}
for (size_t i = 0 ; i != type.size(); ++i) {
sal_Unicode c = type[i];
if (c == '.' ) {
if (!scoped || i == 0 || i == type.size() - 1
|| type[i - 1 ] == '.' )
{
return false ;
}
} else if (!rtl::isAsciiAlphanumeric(c) && c != '_' ) {
return false ;
}
}
return true ;
}
void checkTypeName(
rtl::Reference< MappedFile > const & file, std::u16string_view type)
{
std::u16string_view nucl(type);
bool args = false ;
while (o3tl::starts_with(nucl, u"[]" , &nucl)) {}
size_t i = nucl.find('<' );
if (i != std::u16string_view::npos) {
std::u16string_view tmpl(nucl.substr(0 , i));
do {
++i; // skip '<' or ','
size_t j = i;
for (size_t level = 0 ; j != nucl.size(); ++j) {
sal_Unicode c = nucl[j];
if (c == ',' ) {
if (level == 0 ) {
break ;
}
} else if (c == '<' ) {
++level;
} else if (c == '>' ) {
if (level == 0 ) {
break ;
}
--level;
}
}
if (j != nucl.size()) {
checkTypeName(file, nucl.substr(i, j - i));
args = true ;
}
i = j;
} while (i != nucl.size() && nucl[i] != '>' );
if (i != nucl.size() - 1 || nucl[i] != '>' || !args) {
tmpl = {}; // bad input
}
nucl = tmpl;
}
if (isSimpleType(nucl) ? args : !isIdentifier(nucl, true )) {
throw FileFormatException(
file->uri, OUString::Concat("UNOIDL format: bad type \" ") + type + " \"" );
}
}
void checkEntityName(
rtl::Reference< MappedFile > const & file, std::u16string_view name)
{
if (isSimpleType(name) || !isIdentifier(name, false )) {
throw FileFormatException(
file->uri, OUString::Concat("UNOIDL format: bad entity name \" ") + name + " \"" );
}
}
}
MappedFile::MappedFile(OUString fileUrl): uri(std::move(fileUrl)), handle(nullptr) {
oslFileError e = osl_openFile(uri.pData, &handle, osl_File_OpenFlag_Read);
switch (e) {
case osl_File_E_None:
break ;
case osl_File_E_NOENT:
throw NoSuchFileException(uri);
default :
throw FileFormatException(uri, "cannot open: " + OUString::number(e));
}
e = osl_getFileSize(handle, &size);
if (e == osl_File_E_None) {
e = osl_mapFile(
handle, &address, size, 0 , osl_File_MapFlag_RandomAccess);
}
if (e != osl_File_E_None) {
oslFileError e2 = osl_closeFile(handle);
SAL_WARN_IF(
e2 != osl_File_E_None, "unoidl" ,
"cannot close " << uri << ": " << +e2);
throw FileFormatException(uri, "cannot mmap: " + OUString::number(e));
}
}
sal_uInt8 MappedFile::read8(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 1 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 8-bit value too large" _ustr);
}
return get8(offset);
}
sal_uInt16 MappedFile::read16(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 2 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 16-bit value too large" _ustr);
}
return get16(offset);
}
sal_uInt32 MappedFile::read32(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 4 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 32-bit value too large" _ustr);
}
return get32(offset);
}
sal_uInt64 MappedFile::read64(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 8 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 64-bit value too large" _ustr);
}
return get64(offset);
}
float MappedFile::readIso60599Binary32(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 4 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 32-bit value too large" _ustr);
}
return getIso60599Binary32(offset);
}
double MappedFile::readIso60599Binary64(sal_uInt32 offset) const {
assert(size >= 8 );
if (offset > size - 8 ) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 64-bit value too large" _ustr);
}
return getIso60599Binary64(offset);
}
OUString MappedFile::readNulName(sal_uInt32 offset) {
if (offset > size) {
throw FileFormatException(
uri, u"UNOIDL format: offset for string too large" _ustr);
}
sal_uInt64 end = offset;
for (;; ++end) {
if (end == size) {
throw FileFormatException(
uri, u"UNOIDL format: string misses trailing NUL" _ustr);
}
if (static_cast < char const * >(address)[end] == 0 ) {
break ;
}
}
if (end - offset > SAL_MAX_INT32) {
throw FileFormatException(uri, u"UNOIDL format: string too long" _ustr);
}
OUString name;
if (!rtl_convertStringToUString(
&name.pData, static_cast < char const * >(address) + offset,
end - offset, RTL_TEXTENCODING_ASCII_US,
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
{
throw FileFormatException(uri, u"UNOIDL format: name is not ASCII" _ustr);
}
checkEntityName(this , name);
return name;
}
MappedFile::~MappedFile() {
oslFileError e = osl_unmapMappedFile(handle, address, size);
SAL_WARN_IF(e != osl_File_E_None, "unoidl" , "cannot unmap: " << +e);
e = osl_closeFile(handle);
SAL_WARN_IF(e != osl_File_E_None, "unoidl" , "cannot close: " << +e);
}
sal_uInt8 MappedFile::get8(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 1 );
return static_cast < char const * >(address)[offset];
}
sal_uInt16 MappedFile::get16(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 2 );
return reinterpret_cast < Memory16 const * >(
static_cast < char const * >(address) + offset)->getUnsigned16();
}
sal_uInt32 MappedFile::get32(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 4 );
return reinterpret_cast < Memory32 const * >(
static_cast < char const * >(address) + offset)->getUnsigned32();
}
sal_uInt64 MappedFile::get64(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 8 );
return reinterpret_cast < Memory64 const * >(
static_cast < char const * >(address) + offset)->getUnsigned64();
}
float MappedFile::getIso60599Binary32(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 4 );
return reinterpret_cast < Memory32 const * >(
static_cast < char const * >(address) + offset)->getIso60599Binary32();
}
double MappedFile::getIso60599Binary64(sal_uInt32 offset) const {
assert(size >= 8 );
assert(offset <= size - 8 );
return reinterpret_cast < Memory64 const * >(
static_cast < char const * >(address) + offset)->getIso60599Binary64();
}
OUString MappedFile::readIdxString(
sal_uInt32 * offset, rtl_TextEncoding encoding) const
{
assert(offset != nullptr);
sal_uInt32 len = read32(*offset);
sal_uInt32 off;
if ((len & 0 x80000000) == 0 ) {
off = *offset;
*offset += 4 + len;
} else {
*offset += 4 ;
off = len & ~0 x80000000;
len = read32(off);
if ((len & 0 x80000000) != 0 ) {
throw FileFormatException(
uri, u"UNOIDL format: string length high bit set" _ustr);
}
}
if (len > SAL_MAX_INT32 || len > size - off - 4 ) {
throw FileFormatException(
uri, u"UNOIDL format: size of string is too large" _ustr);
}
OUString name;
if (!rtl_convertStringToUString(
&name.pData, static_cast < char const * >(address) + off + 4 , len,
encoding,
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
{
throw FileFormatException(
uri, u"UNOIDL format: string bytes do not match encoding" _ustr);
}
return name;
}
// sizeof (MapEntry) == 8
struct MapEntry {
Memory32 name;
Memory32 data;
};
static bool operator <(const Map& map1, const Map& map2) {
return map1.begin < map2.begin
|| (map1.begin == map2.begin && map1.size < map2.size);
}
namespace {
enum Compare { COMPARE_LESS, COMPARE_GREATER, COMPARE_EQUAL };
Compare compare(
rtl::Reference< MappedFile > const & file, std::u16string_view name,
sal_Int32 nameOffset, sal_Int32 nameLength, MapEntry const * entry)
{
assert(file.is());
assert(entry != nullptr);
sal_uInt32 off = entry->name.getUnsigned32();
if (off > file->size - 1 ) { // at least a trailing NUL
throw FileFormatException(
file->uri, u"UNOIDL format: string offset too large" _ustr);
}
assert(nameLength >= 0 );
sal_uInt64 min = std::min(
static_cast < sal_uInt64 >(nameLength), file->size - off);
for (sal_uInt64 i = 0 ; i != min; ++i) {
sal_Unicode c1 = name[nameOffset + i];
sal_Unicode c2 = static_cast < unsigned char const * >(file->address)[
off + i];
if (c1 < c2) {
return COMPARE_LESS;
} else if (c1 > c2 || c2 == 0 ) {
// ...the "|| c2 == 0" is for the odd case where name erroneously
// contains NUL characters
return COMPARE_GREATER;
}
}
if (static_cast < sal_uInt64 >(nameLength) == min) {
if (file->size - off == min) {
throw FileFormatException(
file->uri, u"UNOIDL format: string misses trailing NUL" _ustr);
}
return
static_cast < unsigned char const * >(file->address)[off + min] == 0
? COMPARE_EQUAL : COMPARE_LESS;
} else {
return COMPARE_GREATER;
}
}
sal_uInt32 findInMap(
rtl::Reference< MappedFile > const & file, MapEntry const * mapBegin,
sal_uInt32 mapSize, OUString const & name, sal_Int32 nameOffset,
sal_Int32 nameLength)
{
if (mapSize == 0 ) {
return 0 ;
}
sal_uInt32 n = mapSize / 2 ;
MapEntry const * p = mapBegin + n;
switch (compare(file, name, nameOffset, nameLength, p)) {
case COMPARE_LESS:
return findInMap(file, mapBegin, n, name, nameOffset, nameLength);
case COMPARE_GREATER:
return findInMap(
file, p + 1 , mapSize - n - 1 , name, nameOffset, nameLength);
default : // COMPARE_EQUAL
break ;
}
sal_uInt32 off = mapBegin[n].data.getUnsigned32();
if (off == 0 ) {
throw FileFormatException(
file->uri, u"UNOIDL format: map entry data offset is null" _ustr);
}
return off;
}
std::vector< OUString > readAnnotations(
bool annotated, rtl::Reference< MappedFile > const & file,
sal_uInt32 offset, sal_uInt32 * newOffset = nullptr)
{
std::vector< OUString > ans;
if (annotated) {
sal_uInt32 n = file->read32(offset);
offset += 4 ;
for (sal_uInt32 i = 0 ; i != n; ++i) {
ans.push_back(file->readIdxString(&offset));
}
}
if (newOffset != nullptr) {
*newOffset = offset;
}
return ans;
}
ConstantValue readConstant(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
sal_uInt32 * newOffset, bool * annotated)
{
assert(file.is());
int v = file->read8(offset);
int type = v & 0 x7F;
if (annotated != nullptr) {
*annotated = (v & 0 x80) != 0 ;
}
switch (type) {
case 0 : // BOOLEAN
v = file->read8(offset + 1 );
if (newOffset != nullptr) {
*newOffset = offset + 2 ;
}
switch (v) {
case 0 :
return ConstantValue(false );
case 1 :
return ConstantValue(true );
default :
throw FileFormatException(
file->uri,
("UNOIDL format: bad boolean constant value "
+ OUString::number(v)));
}
case 1 : // BYTE
if (newOffset != nullptr) {
*newOffset = offset + 2 ;
}
return ConstantValue(static_cast < sal_Int8 >(file->read8(offset + 1 )));
//TODO: implementation-defined behavior of conversion from sal_uInt8
// to sal_Int8 relies on two's complement representation
case 2 : // SHORT
if (newOffset != nullptr) {
*newOffset = offset + 3 ;
}
return ConstantValue(
static_cast < sal_Int16 >(file->read16(offset + 1 )));
//TODO: implementation-defined behavior of conversion from
// sal_uInt16 to sal_Int16 relies on two's complement representation
case 3 : // UNSIGNED SHORT
if (newOffset != nullptr) {
*newOffset = offset + 3 ;
}
return ConstantValue(file->read16(offset + 1 ));
case 4 : // LONG
if (newOffset != nullptr) {
*newOffset = offset + 5 ;
}
return ConstantValue(
static_cast < sal_Int32 >(file->read32(offset + 1 )));
//TODO: implementation-defined behavior of conversion from
// sal_uInt32 to sal_Int32 relies on two's complement representation
case 5 : // UNSIGNED LONG
if (newOffset != nullptr) {
*newOffset = offset + 5 ;
}
return ConstantValue(file->read32(offset + 1 ));
case 6 : // HYPER
if (newOffset != nullptr) {
*newOffset = offset + 9 ;
}
return ConstantValue(
static_cast < sal_Int64 >(file->read64(offset + 1 )));
//TODO: implementation-defined behavior of conversion from
// sal_uInt64 to sal_Int64 relies on two's complement representation
case 7 : // UNSIGNED HYPER
if (newOffset != nullptr) {
*newOffset = offset + 9 ;
}
return ConstantValue(file->read64(offset + 1 ));
case 8 : // FLOAT
if (newOffset != nullptr) {
*newOffset = offset + 5 ;
}
return ConstantValue(file->readIso60599Binary32(offset + 1 ));
case 9 : // DOUBLE
if (newOffset != nullptr) {
*newOffset = offset + 9 ;
}
return ConstantValue(file->readIso60599Binary64(offset + 1 ));
default :
throw FileFormatException(
file->uri,
"UNOIDL format: bad constant type byte " + OUString::number(v));
}
}
rtl::Reference< Entity > readEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
std::set<Map> && trace);
class UnoidlModuleEntity;
class UnoidlCursor: public MapCursor {
public :
UnoidlCursor(
rtl::Reference< MappedFile > file,
rtl::Reference<UnoidlProvider> reference1,
rtl::Reference<UnoidlModuleEntity> reference2,
NestedMap const & map):
file_(std::move(file)), reference1_(std::move(reference1)), reference2_(std::move(reference2)),
map_(map), index_(0 )
{}
private :
virtual ~UnoidlCursor() noexcept override {}
virtual rtl::Reference< Entity > getNext(OUString * name) override;
rtl::Reference< MappedFile > file_;
rtl::Reference<UnoidlProvider> reference1_; // HACK to keep alive whatever
rtl::Reference<UnoidlModuleEntity> reference2_; // owner of map_
NestedMap const & map_;
sal_uInt32 index_;
};
rtl::Reference< Entity > UnoidlCursor::getNext(OUString * name) {
assert(name != nullptr);
rtl::Reference< Entity > ent;
if (index_ != map_.map.size) {
*name = file_->readNulName(map_.map.begin[index_].name.getUnsigned32());
ent = readEntity(
file_, map_.map.begin[index_].data.getUnsigned32(), std::set(map_.trace));
++index_;
}
return ent;
}
class UnoidlModuleEntity: public ModuleEntity {
public :
UnoidlModuleEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 mapOffset,
sal_uInt32 mapSize, std::set<Map> && trace):
file_(file)
{
assert(file.is());
map_.map.begin = reinterpret_cast <MapEntry const *>(
static_cast <char const *>(file_->address) + mapOffset);
map_.map.size = mapSize;
map_.trace = std::move(trace);
if (!map_.trace.insert(map_.map).second) {
throw FileFormatException(
file_->uri, u"UNOIDL format: recursive map" _ustr);
}
}
private :
virtual ~UnoidlModuleEntity() noexcept override {}
virtual std::vector< OUString > getMemberNames() const override;
virtual rtl::Reference< MapCursor > createCursor() const override {
return new UnoidlCursor(
file_, rtl::Reference<UnoidlProvider>(),
const_cast <UnoidlModuleEntity *>(this ), map_);
}
rtl::Reference< MappedFile > file_;
NestedMap map_;
};
std::vector< OUString > UnoidlModuleEntity::getMemberNames() const {
std::vector< OUString > names;
for (sal_uInt32 i = 0 ; i != map_.map.size; ++i) {
names.push_back(
file_->readNulName(map_.map.begin[i].name.getUnsigned32()));
}
return names;
}
rtl::Reference< Entity > readEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
std::set<Map> && trace)
{
assert(file.is());
int v = file->read8(offset);
int type = v & 0 x3F;
bool published = (v & 0 x80) != 0 ;
bool annotated = (v & 0 x40) != 0 ;
bool flag = (v & 0 x20) != 0 ;
switch (type) {
case 0 : // module
{
if (v != 0 ) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad module type byte "
+ OUString::number(v)));
}
sal_uInt32 n = file->read32(offset + 1 );
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri, u"UNOIDL format: too many items in module" _ustr);
}
if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
// cannot overflow
{
throw FileFormatException(
file->uri,
u"UNOIDL format: module map offset + size too large" _ustr);
}
return new UnoidlModuleEntity(file, offset + 5 , n, std::move(trace));
}
case 1 : // enum type
{
sal_uInt32 n = file->read32(offset + 1 );
if (n == 0 ) {
throw FileFormatException(
file->uri, u"UNOIDL format: enum type with no members" _ustr);
}
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri, u"UNOIDL format: too many members of enum type" _ustr);
}
offset += 5 ;
std::vector< EnumTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
sal_Int32 memValue = static_cast < sal_Int32 >(
file->read32(offset));
//TODO: implementation-defined behavior of conversion from
// sal_uInt32 to sal_Int32 relies on two's complement
// representation
offset += 4 ;
mems.emplace_back(
memName, memValue,
readAnnotations(annotated, file, offset, &offset));
}
return new EnumTypeEntity(
published, std::move(mems), readAnnotations(annotated, file, offset));
}
case 2 : // plain struct type without base
case 2 | 0 x20: // plain struct type with base
{
++offset;
OUString base;
if (flag) {
base = file->readIdxName(&offset);
if (base.isEmpty()) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: empty base type name of plain struct"
" type" _ustr));
}
checkTypeName(file, base);
}
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct members of plain struct"
" type" _ustr));
}
offset += 4 ;
std::vector< PlainStructTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
mems.emplace_back(
memName, memType,
readAnnotations(annotated, file, offset, &offset));
}
return new PlainStructTypeEntity(
published, base, std::move(mems),
readAnnotations(annotated, file, offset));
}
case 3 : // polymorphic struct type template
{
sal_uInt32 n = file->read32(offset + 1 );
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many type parameters of polymorphic"
" struct type template" _ustr));
}
offset += 5 ;
std::vector< OUString > params;
params.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString param(file->readIdxName(&offset));
checkEntityName(file, param);
params.push_back(param);
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many members of polymorphic struct"
" type template" _ustr));
}
offset += 4 ;
std::vector< PolymorphicStructTypeTemplateEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
v = file->read8(offset);
++offset;
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
if (v > 1 ) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad flags " + OUString::number(v)
+ " for member " + memName
+ " of polymorphic struct type template" ));
}
mems.emplace_back(
memName, memType, v == 1 ,
readAnnotations(annotated, file, offset, &offset));
}
return new PolymorphicStructTypeTemplateEntity(
published, std::move(params), std::move(mems),
readAnnotations(annotated, file, offset));
}
case 4 : // exception type without base
case 4 | 0 x20: // exception type with base
{
++offset;
OUString base;
if (flag) {
base = file->readIdxName(&offset);
if (base.isEmpty()) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: empty base type name of exception"
" type" _ustr));
}
checkTypeName(file, base);
}
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
u"UNOIDL format: too many direct members of exception type" _ustr);
}
offset += 4 ;
std::vector< ExceptionTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
mems.emplace_back(
memName, memType,
readAnnotations(annotated, file, offset, &offset));
}
return new ExceptionTypeEntity(
published, base, std::move(mems),
readAnnotations(annotated, file, offset));
}
case 5 : // interface type
{
sal_uInt32 n = file->read32(offset + 1 );
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory bases of"
" interface type" _ustr));
}
offset += 5 ;
std::vector< AnnotatedReference > mandBases;
mandBases.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandBases.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional bases of"
" interface type" _ustr));
}
offset += 4 ;
std::vector< AnnotatedReference > optBases;
optBases.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optBases.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
sal_uInt32 nAttrs = file->read32(offset);
if (nAttrs > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct attributes of interface"
" type" _ustr));
}
offset += 4 ;
std::vector< InterfaceTypeEntity::Attribute > attrs;
attrs.reserve(nAttrs);
for (sal_uInt32 i = 0 ; i != nAttrs; ++i) {
v = file->read8(offset);
++offset;
OUString attrName(file->readIdxName(&offset));
checkEntityName(file, attrName);
OUString attrType(file->readIdxName(&offset));
checkTypeName(file, attrType);
if (v > 0 x03) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad flags for direct attribute "
+ attrName + " of interface type" ));
}
std::vector< OUString > getExcs;
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many getter exceptions for direct"
" attribute " + attrName + " of interface type" ));
}
offset += 4 ;
getExcs.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
getExcs.push_back(exc);
}
std::vector< OUString > setExcs;
if ((v & 0 x02) == 0 ) {
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many setter exceptions for"
" direct attribute " + attrName
+ " of interface type" ));
}
offset += 4 ;
setExcs.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
setExcs.push_back(exc);
}
}
attrs.emplace_back(
attrName, attrType, (v & 0 x01) != 0 , (v & 0 x02) != 0 ,
std::move(getExcs), std::move(setExcs),
readAnnotations(annotated, file, offset, &offset));
}
sal_uInt32 nMeths = file->read32(offset);
if (nMeths > SAL_MAX_INT32 - nAttrs) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct attributes and methods of"
" interface type" _ustr));
}
offset += 4 ;
std::vector< InterfaceTypeEntity::Method > meths;
meths.reserve(nMeths);
for (sal_uInt32 i = 0 ; i != nMeths; ++i) {
OUString methName(file->readIdxName(&offset));
checkEntityName(file, methName);
OUString methType(file->readIdxName(&offset));
checkTypeName(file, methType);
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many parameters for method "
+ methName + " of interface type" ));
}
offset += 4 ;
std::vector< InterfaceTypeEntity::Method::Parameter > params;
params.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
v = file->read8(offset);
++offset;
OUString paramName(file->readIdxName(&offset));
checkEntityName(file, paramName);
OUString paramType(file->readIdxName(&offset));
checkTypeName(file, paramType);
InterfaceTypeEntity::Method::Parameter::Direction dir;
switch (v) {
case 0 :
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_IN;
break ;
case 1 :
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_OUT;
break ;
case 2 :
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_IN_OUT;
break ;
default :
throw FileFormatException(
file->uri,
("UNOIDL format: bad direction "
+ OUString::number(v) + " of parameter "
+ paramName + " for method " + methName
+ " of interface type" ));
}
params.emplace_back(paramName, paramType, dir);
}
std::vector< OUString > excs;
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many exceptions for method "
+ methName + " of interface type" ));
}
offset += 4 ;
excs.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
excs.push_back(exc);
}
meths.emplace_back(
methName, methType, std::move(params), std::move(excs),
readAnnotations(annotated, file, offset, &offset));
}
return new InterfaceTypeEntity(
published, std::move(mandBases), std::move(optBases), std::move(attrs), std::move(meths),
readAnnotations(annotated, file, offset));
}
case 6 : // typedef
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new TypedefEntity(
published, base, readAnnotations(annotated, file, offset));
}
case 7 : // constant group
{
sal_uInt32 n = file->read32(offset + 1 );
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
u"UNOIDL format: too many constants in constant group" _ustr);
}
if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
// cannot overflow
{
throw FileFormatException(
file->uri,
(u"UNOIDL format: constant group map offset + size too"
" large" _ustr));
}
MapEntry const * p = reinterpret_cast < MapEntry const * >(
static_cast < char const * >(file->address) + offset + 5 );
std::vector< ConstantGroupEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
sal_uInt32 off = p[i].data.getUnsigned32();
bool ann;
ConstantValue val(readConstant(file, off, &off, &ann));
mems.emplace_back(
file->readNulName(p[i].name.getUnsigned32()), val,
readAnnotations(ann, file, off));
}
return new ConstantGroupEntity(
published, std::move(mems),
readAnnotations(annotated, file, offset + 5 + 8 * n));
}
case 8 : // single-interface--based service without default constructor
case 8 | 0 x20: // single-interface--based service with default constructor
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
std::vector< SingleInterfaceBasedServiceEntity::Constructor > ctors;
if (flag) {
ctors.push_back(
SingleInterfaceBasedServiceEntity::Constructor());
} else {
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many constructors of"
" single-interface--based service" _ustr));
}
offset += 4 ;
ctors.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString ctorName(file->readIdxName(&offset));
checkEntityName(file, ctorName);
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many parameters for"
" constructor " + ctorName
+ " of single-interface--based service" ));
}
offset += 4 ;
std::vector<
SingleInterfaceBasedServiceEntity::Constructor::
Parameter > params;
params.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
v = file->read8(offset);
++offset;
OUString paramName(file->readIdxName(&offset));
checkEntityName(file, paramName);
OUString paramType(file->readIdxName(&offset));
checkTypeName(file, paramType);
bool rest;
switch (v) {
case 0 :
rest = false ;
break ;
case 0 x04:
rest = true ;
break ;
default :
throw FileFormatException(
file->uri,
("UNOIDL format: bad mode "
+ OUString::number(v) + " of parameter "
+ paramName + " for constructor " + ctorName
+ " of single-interface--based service" ));
}
params.emplace_back(paramName, paramType, rest);
}
std::vector< OUString > excs;
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many exceptions for"
" constructor " + ctorName
+ " of single-interface--based service" ));
}
offset += 4 ;
excs.reserve(m);
for (sal_uInt32 j = 0 ; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
excs.push_back(exc);
}
ctors.push_back(
SingleInterfaceBasedServiceEntity::Constructor(
ctorName, std::move(params), std::move(excs),
readAnnotations(annotated, file, offset, &offset)));
}
}
return new SingleInterfaceBasedServiceEntity(
published, base, std::move(ctors),
readAnnotations(annotated, file, offset));
}
case 9 : // accumulation-based service
{
sal_uInt32 n = file->read32(offset + 1 );
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory service bases of"
" accumulation-based service" _ustr));
}
offset += 5 ;
std::vector< AnnotatedReference > mandServs;
mandServs.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandServs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional service bases of"
" accumulation-based service" _ustr));
}
offset += 4 ;
std::vector< AnnotatedReference > optServs;
optServs.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optServs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory interface bases"
" of accumulation-based service" _ustr));
}
offset += 4 ;
std::vector< AnnotatedReference > mandIfcs;
mandIfcs.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandIfcs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional interface bases"
" of accumulation-based service" _ustr));
}
offset += 4 ;
std::vector< AnnotatedReference > optIfcs;
optIfcs.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optIfcs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct properties of"
" accumulation-based service" _ustr));
}
offset += 4 ;
std::vector< AccumulationBasedServiceEntity::Property > props;
props.reserve(n);
for (sal_uInt32 i = 0 ; i != n; ++i) {
sal_uInt16 attrs = file->read16(offset);
offset += 2 ;
OUString propName(file->readIdxName(&offset));
checkEntityName(file, propName);
OUString propType(file->readIdxName(&offset));
checkTypeName(file, propType);
if (attrs > 0 x01FF) { // see css.beans.PropertyAttribute
throw FileFormatException(
file->uri,
("UNOIDL format: bad mode " + OUString::number(v)
+ " of property " + propName
+ " for accumulation-based service" ));
}
props.emplace_back(
propName, propType,
static_cast <
AccumulationBasedServiceEntity::Property::Attributes >(
attrs),
readAnnotations(annotated, file, offset, &offset));
}
return new AccumulationBasedServiceEntity(
published, std::move(mandServs), std::move(optServs), std::move(mandIfcs), std::move(optIfcs), std::move(props),
readAnnotations(annotated, file, offset));
}
case 10 : // interface-based singleton
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new InterfaceBasedSingletonEntity(
published, base, readAnnotations(annotated, file, offset));
}
case 11 : // service-based singleton
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new ServiceBasedSingletonEntity(
published, base, readAnnotations(annotated, file, offset));
}
default :
throw FileFormatException(
file->uri, "UNOIDL format: bad type byte " + OUString::number(v));
}
}
}
UnoidlProvider::UnoidlProvider(OUString const & uri): file_(new MappedFile(uri))
{
if (file_->size < 8 || std::memcmp(file_->address, "UNOIDL\xFF\0" , 8 ) != 0 )
{
throw FileFormatException(
file_->uri,
u"UNOIDL format: does not begin with magic UNOIDL\\xFF and version"
" 0" _ustr);
}
sal_uInt32 off = file_->read32(8 );
map_.map.size = file_->read32(12 );
if (off + 8 * sal_uInt64(map_.map.size) > file_->size) { // cannot overflow
throw FileFormatException(
file_->uri, u"UNOIDL format: root map offset + size too large" _ustr);
}
map_.map.begin = reinterpret_cast < MapEntry const * >(
static_cast < char const * >(file_->address) + off);
map_.trace.insert(map_.map);
}
rtl::Reference< MapCursor > UnoidlProvider::createRootCursor() const {
return new UnoidlCursor(
file_, const_cast <UnoidlProvider *>(this ),
rtl::Reference<UnoidlModuleEntity>(), map_);
}
rtl::Reference< Entity > UnoidlProvider::findEntity(OUString const & name) const
{
NestedMap map(map_);
bool cgroup = false ;
for (sal_Int32 i = 0 ;;) {
sal_Int32 j = name.indexOf('.' , i);
if (j == -1 ) {
j = name.getLength();
}
sal_Int32 off = findInMap(
file_, map.map.begin, map.map.size, name, i, j - i);
if (off == 0 ) {
return rtl::Reference< Entity >();
}
if (j == name.getLength()) {
return cgroup
? rtl::Reference< Entity >()
: readEntity(file_, off, std::set(map.trace));
}
if (cgroup) {
return rtl::Reference< Entity >();
//TODO: throw an exception instead here, where the segments of a
// constant's name are a prefix of the requested name's
// segments?
}
int v = file_->read8(off);
if (v != 0 ) { // module
if ((v & 0 x3F) == 7 ) { // constant group
cgroup = true ;
} else {
return rtl::Reference< Entity >();
//TODO: throw an exception instead here, where the segments
// of a non-module, non-constant-group entity's name are a
// prefix of the requested name's segments?
}
}
map.map.size = file_->read32(off + 1 );
if (sal_uInt64(off) + 5 + 8 * sal_uInt64(map.map.size) > file_->size)
// cannot overflow
{
throw FileFormatException(
file_->uri, u"UNOIDL format: map offset + size too large" _ustr);
}
map.map.begin = reinterpret_cast < MapEntry const * >(
static_cast < char const * >(file_->address) + off + 5 );
if (!map.trace.insert(map.map).second) {
throw FileFormatException(
file_->uri, u"UNOIDL format: recursive map" _ustr);
}
i = j + 1 ;
}
}
UnoidlProvider::~UnoidlProvider() noexcept {}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=95 H=90 G=92
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
letze Version des Elbe Quellennavigators
Jenseits des Üblichen ....
Besucher
Statistik
#Sources=1126864
#Domains=1897691