diff options
Diffstat (limited to '')
-rw-r--r-- | basic/source/classes/image.cxx | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/basic/source/classes/image.cxx b/basic/source/classes/image.cxx new file mode 100644 index 000000000..e5f9ac3f5 --- /dev/null +++ b/basic/source/classes/image.cxx @@ -0,0 +1,718 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/stream.hxx> +#include <tools/tenccvt.hxx> +#include <osl/thread.h> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <basic/sbx.hxx> +#include <sb.hxx> +#include <sbxprop.hxx> +#include <string.h> +#include <image.hxx> +#include <codegen.hxx> +#include <memory> +#include <string_view> + +SbiImage::SbiImage() + : bError(false) + , nFlags(SbiImageFlags::NONE) + , nStringSize(0) + , nDimBase(0) + , eCharSet(osl_getThreadTextEncoding()) + , nStringIdx(0) + , nStringOff(0) + , bInit(false) + , bFirstInit(true) +{ +} + +SbiImage::~SbiImage() +{ +} + +void SbiImage::Clear() +{ + mvStringOffsets.clear(); + pStrings.reset(); + aCode.clear(); + aLegacyPCode.clear(); + nFlags = SbiImageFlags::NONE; + nStringSize= 0; + eCharSet = osl_getThreadTextEncoding(); + nDimBase = 0; + bError = false; +} + +// Open Record +static sal_uInt64 SbiOpenRecord( SvStream& r, FileOffset nSignature, sal_uInt16 nElem ) +{ + sal_uInt64 nPos = r.Tell(); + r.WriteUInt16( static_cast<sal_uInt16>( nSignature ) ) + .WriteInt32( 0 ).WriteUInt16( nElem ); + return nPos; +} + +// Close Record +static void SbiCloseRecord( SvStream& r, sal_uInt64 nOff ) +{ + sal_uInt64 nPos = r.Tell(); + r.Seek( nOff + 2 ); + r.WriteInt32(nPos - nOff - 8 ); + r.Seek( nPos ); +} + +constexpr sal_uInt32 nUnicodeDataMagicNumber = 0x556E6920; // "Uni " BE + +static bool GetToUnicodePoolData(SvStream& r, sal_uInt64 nLen, sal_uInt64 nNext) +{ + const auto nPos = r.Tell(); + // Check space for legacy data, magic number and Unicode data + bool bResult = nPos + nLen + sizeof(sal_uInt32) + nLen * sizeof(sal_Unicode) <= nNext; + if (bResult) + { + r.SeekRel(nLen); // Skip legacy data + sal_uInt32 nMagic = 0; + r.ReadUInt32(nMagic); + if (nMagic != nUnicodeDataMagicNumber) + { + r.Seek(nPos); // return + bResult = false; + } + } + return bResult; +} + +bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion ) +{ + + sal_uInt16 nSign, nCount; + sal_uInt32 nLen; + + Clear(); + // Read Master-Record + r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount ); + sal_uInt64 nLast = r.Tell() + nLen; + bool bBadVer = false; + if( nSign == static_cast<sal_uInt16>( FileOffset::Module ) ) + { + sal_uInt32 nCharSet; // System charset + sal_uInt32 lDimBase; + sal_uInt16 nTmpFlags; + sal_uInt16 nReserved1; + sal_uInt32 nReserved2; + sal_uInt32 nReserved3; + r.ReadUInt32( nVersion ).ReadUInt32( nCharSet ).ReadUInt32( lDimBase ) + .ReadUInt16( nTmpFlags ).ReadUInt16( nReserved1 ).ReadUInt32( nReserved2 ).ReadUInt32( nReserved3 ); + nFlags = static_cast<SbiImageFlags>(nTmpFlags); + eCharSet = nCharSet; + eCharSet = GetSOLoadTextEncoding( eCharSet ); + bBadVer = ( nVersion > B_CURVERSION ); + nDimBase = static_cast<sal_uInt16>(lDimBase); + } + + bool bLegacy = ( nVersion < B_EXT_IMG_VERSION ); + + sal_uInt64 nNext; + while( ( nNext = r.Tell() ) < nLast ) + { + + r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount ); + nNext += nLen + 8; + if( r.GetError() == ERRCODE_NONE ) + { + switch( static_cast<FileOffset>( nSign ) ) + { + case FileOffset::Name: + aName = r.ReadUniOrByteString(eCharSet); + break; + case FileOffset::Comment: + aComment = r.ReadUniOrByteString(eCharSet ); + break; + case FileOffset::Source: + { + aOUSource = r.ReadUniOrByteString(eCharSet); + break; + } + case FileOffset::ExtSource: + { + //assuming an empty string with just the lead 32bit/16bit len indicator + const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2; + const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize; + if (nCount > nMaxStrings) + { + SAL_WARN("basic", "Parsing error: " << nMaxStrings << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxStrings; + } + for( sal_uInt16 j = 0; j < nCount; ++j) + { + aOUSource += r.ReadUniOrByteString(eCharSet); + } + break; + } + case FileOffset::PCode: + if( bBadVer ) break; + aCode.resize(nLen); + r.ReadBytes(aCode.data(), aCode.size()); + if ( bLegacy ) + { + aLegacyPCode = std::move(aCode); + + PCodeBuffConvertor<sal_uInt16, sal_uInt32> aLegacyToNew(aLegacyPCode.data(), + aLegacyPCode.size()); + aLegacyToNew.convert(); + aCode = aLegacyToNew.GetBuffer(); + // we don't release the legacy buffer + // right now, that's because the module + // needs it to fix up the method + // nStart members. When that is done + // the module can release the buffer + // or it can wait until this routine + // is called again or when this class // destructs all of which will trigger + // release of the buffer. + } + break; + case FileOffset::Publics: + case FileOffset::PoolDir: + case FileOffset::SymPool: + case FileOffset::LineRanges: + break; + case FileOffset::StringPool: + { + // the data layout is: nCount of 32-bit offsets into both legacy 1-byte char stream + // and resulting char buffer (1:1 correspondence assumed; 16 of 32 bits used); + // 32-bit length N of following 1-byte char stream (16 bits used); N bytes of 1-byte + // char stream; then optional magic number and stream of N sal_Unicode characters. + + if( bBadVer ) break; + //assuming an empty string with just the lead 32bit len indicator + const sal_uInt64 nMinStringSize = 4; + const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize; + if (nCount > nMaxStrings) + { + SAL_WARN("basic", "Parsing error: " << nMaxStrings << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxStrings; + } + MakeStrings( nCount ); + for (size_t i = 0; i < mvStringOffsets.size() && r.good(); i++) + { + sal_uInt32 nOff; + r.ReadUInt32( nOff ); + mvStringOffsets[ i ] = static_cast<sal_uInt16>(nOff); + } + r.ReadUInt32( nLen ); + if (r.good()) + { + pStrings.reset(new sal_Unicode[ nLen ]); + nStringSize = static_cast<sal_uInt16>(nLen); + + if (GetToUnicodePoolData(r, nLen, nNext)) + { + OUString s = read_uInt16s_ToOUString(r, nLen); + memcpy(pStrings.get(), s.getStr(), s.getLength() * sizeof(sal_Unicode)); + } + else + { + std::unique_ptr<char[]> pByteStrings(new char[nLen]); + r.ReadBytes(pByteStrings.get(), nLen); + for (size_t j = 0; j < mvStringOffsets.size(); j++) + { + sal_uInt16 nOff2 = static_cast<sal_uInt16>(mvStringOffsets[j]); + OUString aStr(pByteStrings.get() + nOff2, strlen(pByteStrings.get() + nOff2), eCharSet); + memcpy(pStrings.get() + nOff2, aStr.getStr(), (aStr.getLength() + 1) * sizeof(sal_Unicode)); + } + } + } + break; + } + case FileOffset::UserTypes: + { + //assuming an empty string with just the lead 32bit/16bit len indicator + const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2; + const sal_uInt64 nMinRecordSize = nMinStringSize + sizeof(sal_Int16); + const sal_uInt64 nMaxRecords = r.remainingSize() / nMinRecordSize; + if (nCount > nMaxRecords) + { + SAL_WARN("basic", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nCount << " claimed, truncating"); + nCount = nMaxRecords; + } + + // User defined types; ref.: SbiParser::DefType + for (sal_uInt16 i = 0; i < nCount; i++) + { + OUString aTypeName = r.ReadUniOrByteString(eCharSet); + + sal_uInt16 nTypeMembers; + r.ReadUInt16(nTypeMembers); + + const sal_uInt64 nMaxTypeMembers = r.remainingSize() / 8; + if (nTypeMembers > nMaxTypeMembers) + { + SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers << + " max possible entries, but " << nTypeMembers << " claimed, truncating"); + nTypeMembers = nMaxTypeMembers; + } + + SbxObject *pType = new SbxObject(aTypeName); + SbxArray *pTypeMembers = pType->GetProperties(); + + for (sal_uInt16 j = 0; j < nTypeMembers; j++) + { + OUString aMemberName = r.ReadUniOrByteString(eCharSet); + + sal_Int16 aIntMemberType; + r.ReadInt16(aIntMemberType); + SbxDataType aMemberType = static_cast< SbxDataType > ( aIntMemberType ); + + SbxProperty *pTypeElem = new SbxProperty( aMemberName, aMemberType ); + + sal_uInt32 aIntFlag; + r.ReadUInt32(aIntFlag); + SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag ); + + pTypeElem->SetFlags(nElemFlags); + + sal_Int16 hasObject; + r.ReadInt16(hasObject); + + if (hasObject == 1) + { + if(aMemberType == SbxOBJECT) + { + // nested user defined types + // declared before use, so it is ok to reference it by name on load + OUString aNestedTypeName = r.ReadUniOrByteString(eCharSet); + SbxObject* pNestedTypeObj = static_cast< SbxObject* >( rTypes->Find( aNestedTypeName, SbxClassType::Object ) ); + if (pNestedTypeObj) + { + SbxObjectRef pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj ); + pTypeElem->PutObject( pCloneObj.get() ); + } + } + else + { + // an array + SbxDimArray* pArray = new SbxDimArray( + static_cast<SbxDataType>(aMemberType & 0x0FFF)); + + sal_Int16 isFixedSize; + r.ReadInt16(isFixedSize); + if (isFixedSize == 1) + pArray->setHasFixedSize( true ); + + sal_Int32 nDims; + r.ReadInt32(nDims); + for (sal_Int32 d = 0; d < nDims; d++) + { + sal_Int32 lBound; + sal_Int32 uBound; + r.ReadInt32(lBound).ReadInt32(uBound); + pArray->unoAddDim(lBound, uBound); + } + + const SbxFlagBits nSavFlags = pTypeElem->GetFlags(); + // need to reset the FIXED flag + // when calling PutObject ( because the type will not match Object ) + pTypeElem->ResetFlag(SbxFlagBits::Fixed); + pTypeElem->PutObject( pArray ); + pTypeElem->SetFlags(nSavFlags); + } + } + + pTypeMembers->Insert(pTypeElem, pTypeMembers->Count()); + + } + + pType->Remove( "Name", SbxClassType::DontCare ); + pType->Remove( "Parent", SbxClassType::DontCare ); + + AddType(pType); + } + break; + } + case FileOffset::ModEnd: + goto done; + default: + break; + } + } + else + { + break; + } + r.Seek( nNext ); + } +done: + r.Seek( nLast ); + if (!r.good()) + { + bError = true; + } + return !bError; +} + +bool SbiImage::Save( SvStream& r, sal_uInt32 nVer ) +{ + bool bLegacy = ( nVer < B_EXT_IMG_VERSION ); + + // detect if old code exceeds legacy limits + // if so, then disallow save + if ( bLegacy && ExceedsLegacyLimits() ) + { + SbiImage aEmptyImg; + aEmptyImg.aName = aName; + aEmptyImg.Save( r, B_LEGACYVERSION ); + return true; + } + // First of all the header + sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 ); + sal_uInt64 nPos; + + eCharSet = GetSOStoreTextEncoding( eCharSet ); + if ( bLegacy ) + { + r.WriteInt32( B_LEGACYVERSION ); + } + else + { + r.WriteInt32( B_CURVERSION ); + } + r .WriteInt32( eCharSet ) + .WriteInt32( nDimBase ) + .WriteInt16( static_cast<sal_uInt16>(nFlags) ) + .WriteInt16( 0 ) + .WriteInt32( 0 ) + .WriteInt32( 0 ); + + // Name? + if (!aName.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Name, 1 ); + r.WriteUniOrByteString( aName, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Comment? + if (!aComment.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Comment, 1 ); + r.WriteUniOrByteString( aComment, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Source? + if (!aOUSource.isEmpty() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::Source, 1 ); + r.WriteUniOrByteString( aOUSource, eCharSet ); + SbiCloseRecord( r, nPos ); + } + // Binary data? + if (aCode.size() && r.good()) + { + nPos = SbiOpenRecord( r, FileOffset::PCode, 1 ); + if ( bLegacy ) + { + PCodeBuffConvertor<sal_uInt32, sal_uInt16> aNewToLegacy(aCode.data(), aCode.size()); + aNewToLegacy.convert(); + aLegacyPCode = aNewToLegacy.GetBuffer(); + r.WriteBytes(aLegacyPCode.data(), aLegacyPCode.size()); + } + else + { + r.WriteBytes(aCode.data(), aCode.size()); + } + SbiCloseRecord( r, nPos ); + } + // String-Pool? + if( !mvStringOffsets.empty() ) + { + nPos = SbiOpenRecord( r, FileOffset::StringPool, mvStringOffsets.size() ); + // For every String: + // sal_uInt32 Offset of the Strings in the Stringblock + for (size_t i = 0; i < mvStringOffsets.size() && r.good(); i++) + { + r.WriteUInt32( mvStringOffsets[ i ] ); + } + // Then the String-Block + std::unique_ptr<char[]> pByteStrings(new char[ nStringSize ]); + for( size_t i = 0; i < mvStringOffsets.size(); i++ ) + { + sal_uInt16 nOff = static_cast<sal_uInt16>(mvStringOffsets[ i ]); + OString aStr(OUStringToOString(std::u16string_view(pStrings.get() + nOff), eCharSet)); + memcpy( pByteStrings.get() + nOff, aStr.getStr(), (aStr.getLength() + 1) * sizeof( char ) ); + } + r.WriteUInt32( nStringSize ); + r.WriteBytes(pByteStrings.get(), nStringSize); + pByteStrings.reset(); + + // Now write magic number and store the same data in UTF-16; this is backward compatible: + // old readers will not read this data after having read legacy data, and will proceed + // straight to the end of the record. So no version restriction here. + r.WriteUInt32(nUnicodeDataMagicNumber); + write_uInt16s_FromOUString(r, std::u16string_view(pStrings.get(), nStringSize)); + + SbiCloseRecord( r, nPos ); + } + // User defined types + if ( rTypes.is() ) + { + sal_uInt32 nTypes = rTypes->Count(); + assert(nTypes <= std::numeric_limits<sal_uInt16>::max()); + if (nTypes > 0 ) + { + nPos = SbiOpenRecord( r, FileOffset::UserTypes, sal::static_int_cast<sal_uInt16>(nTypes) ); + + for (sal_uInt32 i = 0; i < nTypes; i++) + { + SbxObject* pType = static_cast<SbxObject*>(rTypes->Get(i)); + OUString aTypeName = pType->GetClassName(); + + r.WriteUniOrByteString( aTypeName, eCharSet ); + + SbxArray *pTypeMembers = pType->GetProperties(); + sal_uInt32 nTypeMembers = pTypeMembers->Count(); + assert(nTypeMembers <= std::numeric_limits<sal_uInt16>::max()); + + r.WriteInt16(sal::static_int_cast<sal_uInt16>(nTypeMembers)); + + for (sal_uInt32 j = 0; j < nTypeMembers; j++) + { + + SbxProperty* pTypeElem = static_cast<SbxProperty*>(pTypeMembers->Get(j)); + + const OUString& aElemName = pTypeElem->GetName(); + r.WriteUniOrByteString( aElemName, eCharSet ); + + SbxDataType dataType = pTypeElem->GetType(); + r.WriteInt16(dataType); + + SbxFlagBits nElemFlags = pTypeElem->GetFlags(); + r.WriteUInt32(static_cast< sal_uInt32 > (nElemFlags) ); + + SbxBase* pElemObject = pTypeElem->GetObject(); + + if (pElemObject) + { + r.WriteInt16(1); // has elem Object + + if( dataType == SbxOBJECT ) + { + // nested user defined types + // declared before use, so it is ok to reference it by name on load + SbxObject* pNestedType = static_cast< SbxObject* > ( pElemObject ); + r.WriteUniOrByteString( pNestedType->GetClassName(), eCharSet ); + } + else + { + // an array + SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject ); + + bool bFixedSize = pArray->hasFixedSize(); + if (bFixedSize) + r.WriteInt16(1); + else + r.WriteInt16(0); + + sal_Int32 nDims = pArray->GetDims(); + r.WriteInt32(nDims); + + for (sal_Int32 d = 1; d <= nDims; d++) + { + sal_Int32 lBound; + sal_Int32 uBound; + pArray->GetDim(d, lBound, uBound); + r.WriteInt32(lBound).WriteInt32(uBound); + } + } + } + else + r.WriteInt16(0); // no elem Object + + } + } + SbiCloseRecord( r, nPos ); + } + } + // Set overall length + SbiCloseRecord( r, nStart ); + if (!r.good()) + { + bError = true; + } + return !bError; +} + +void SbiImage::MakeStrings( short nSize ) +{ + nStringIdx = 0; + nStringOff = 0; + nStringSize = 1024; + pStrings.reset( new sal_Unicode[ nStringSize ]); + mvStringOffsets.resize(nSize); + if (nSize != 0) { + memset( mvStringOffsets.data(), 0, nSize * sizeof( sal_uInt32 ) ); + } + memset( pStrings.get(), 0, nStringSize * sizeof( sal_Unicode ) ); +} + +// Add a string to StringPool. The String buffer is dynamically +// growing in 1K-Steps +void SbiImage::AddString( const OUString& r ) +{ + if( nStringIdx >= mvStringOffsets.size() ) + { + bError = true; + } + if( bError ) + return; + + sal_Int32 len = r.getLength() + 1; + sal_uInt32 needed = nStringOff + len; + if( needed > 0xFFFFFF00 ) + { + bError = true; // out of mem! + } + else if( needed > nStringSize ) + { + sal_uInt32 nNewLen = needed + 1024; + nNewLen &= 0xFFFFFC00; // trim to 1K border + std::unique_ptr<sal_Unicode[]> p(new sal_Unicode[nNewLen]); + memcpy( p.get(), pStrings.get(), nStringSize * sizeof( sal_Unicode ) ); + pStrings = std::move(p); + nStringSize = sal::static_int_cast< sal_uInt16 >(nNewLen); + } + if( !bError ) + { + mvStringOffsets[ nStringIdx++ ] = nStringOff; + memcpy( pStrings.get() + nStringOff, r.getStr(), len * sizeof( sal_Unicode ) ); + nStringOff = nStringOff + len; + // Last String? The update the size of the buffer + if( nStringIdx >= mvStringOffsets.size() ) + { + nStringSize = nStringOff; + } + } +} + +// Add code block +// The block was fetched by the compiler from class SbBuffer and +// is already created with new. Additionally it contains all Integers +// in Big Endian format, so can be directly read/written. +void SbiImage::AddCode(std::vector<sal_uInt8>&& v) +{ + aCode = std::move(v); +} + +// Add user type +void SbiImage::AddType(SbxObject const * pObject) +{ + if( !rTypes.is() ) + { + rTypes = new SbxArray; + } + SbxObject *pCopyObject = new SbxObject(*pObject); + rTypes->Insert(pCopyObject, rTypes->Count()); +} + +void SbiImage::AddEnum(SbxObject* pObject) // Register enum type +{ + if( !rEnums.is() ) + { + rEnums = new SbxArray; + } + rEnums->Insert(pObject, rEnums->Count()); +} + +// Note: IDs start with 1 +OUString SbiImage::GetString( sal_uInt32 nId, SbxDataType *eType ) const +{ + if( nId && nId <= mvStringOffsets.size() ) + { + sal_uInt32 nOff = mvStringOffsets[ nId - 1 ]; + sal_Unicode* pStr = pStrings.get() + nOff; + + sal_uInt32 nNextOff = (nId < mvStringOffsets.size()) ? mvStringOffsets[ nId ] : nStringSize; + sal_uInt32 nLen = nNextOff - nOff - 1; + // #i42467: Special treatment for vbNullChar + if (*pStr == 0) + { + if( nLen == 1 ) + { + return OUString( u'\0'); + } + } + else + { + // tdf#143707 - check if the data type character was added after the string termination + // symbol. It was added in basic/source/comp/symtbl.cxx. + OUString aOUStr(pStr); + if (eType != nullptr) + { + *eType = SbxSTRING; + if (o3tl::make_unsigned(aOUStr.getLength()) < nLen) + { + const sal_Unicode pTypeChar = *(pStrings.get() + nOff + aOUStr.getLength() + 1); + switch (pTypeChar) + { + // See GetSuffixType in basic/source/comp/scanner.cxx for type characters + case '%': *eType = SbxINTEGER; break; + case '&': *eType = SbxLONG; break; + case '!': *eType = SbxSINGLE; break; + case '#': *eType = SbxDOUBLE; break; + case '@': *eType = SbxCURRENCY; break; + // tdf#142460 - properly handle boolean values in string pool + case 'b': *eType = SbxBOOL; break; + } + } + } + return aOUStr; + } + } + return OUString(); +} + +const SbxObject* SbiImage::FindType (const OUString& aTypeName) const +{ + return rTypes.is() ? static_cast<SbxObject*>(rTypes->Find(aTypeName,SbxClassType::Object)) : nullptr; +} + +sal_uInt16 SbiImage::CalcLegacyOffset( sal_Int32 nOffset ) +{ + return SbiCodeGen::calcLegacyOffSet(aCode.data(), nOffset); +} + +sal_uInt32 SbiImage::CalcNewOffset( sal_Int16 nOffset ) +{ + return SbiCodeGen::calcNewOffSet(aLegacyPCode.data(), nOffset); +} + +void SbiImage::ReleaseLegacyBuffer() +{ + aLegacyPCode.clear(); +} + +bool SbiImage::ExceedsLegacyLimits() +{ + return (nStringSize > 0xFF00) || (CalcLegacyOffset(aCode.size()) > 0xFF00); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |