1/*
2 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <package/hpkg/WriterImplBase.h>
8
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include <algorithm>
16#include <new>
17
18#include <ByteOrder.h>
19#include <File.h>
20
21#include <AutoDeleter.h>
22#include <ZlibCompressionAlgorithm.h>
23#include <ZstdCompressionAlgorithm.h>
24
25#include <package/hpkg/DataReader.h>
26#include <package/hpkg/ErrorOutput.h>
27
28#include <package/hpkg/HPKGDefsPrivate.h>
29
30
31namespace BPackageKit {
32
33namespace BHPKG {
34
35namespace BPrivate {
36
37
38// #pragma mark - AttributeValue
39
40
41WriterImplBase::AttributeValue::AttributeValue()
42	:
43	type(B_HPKG_ATTRIBUTE_TYPE_INVALID),
44	encoding(-1)
45{
46}
47
48
49WriterImplBase::AttributeValue::~AttributeValue()
50{
51}
52
53
54void
55WriterImplBase::AttributeValue::SetTo(int8 value)
56{
57	signedInt = value;
58	type = B_HPKG_ATTRIBUTE_TYPE_INT;
59}
60
61
62void
63WriterImplBase::AttributeValue::SetTo(uint8 value)
64{
65	unsignedInt = value;
66	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
67}
68
69
70void
71WriterImplBase::AttributeValue::SetTo(int16 value)
72{
73	signedInt = value;
74	type = B_HPKG_ATTRIBUTE_TYPE_INT;
75}
76
77
78void
79WriterImplBase::AttributeValue::SetTo(uint16 value)
80{
81	unsignedInt = value;
82	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
83}
84
85
86void
87WriterImplBase::AttributeValue::SetTo(int32 value)
88{
89	signedInt = value;
90	type = B_HPKG_ATTRIBUTE_TYPE_INT;
91}
92
93
94void
95WriterImplBase::AttributeValue::SetTo(uint32 value)
96{
97	unsignedInt = value;
98	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
99}
100
101
102void
103WriterImplBase::AttributeValue::SetTo(int64 value)
104{
105	signedInt = value;
106	type = B_HPKG_ATTRIBUTE_TYPE_INT;
107}
108
109
110void
111WriterImplBase::AttributeValue::SetTo(uint64 value)
112{
113	unsignedInt = value;
114	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
115}
116
117
118void
119WriterImplBase::AttributeValue::SetTo(CachedString* value)
120{
121	string = value;
122	type = B_HPKG_ATTRIBUTE_TYPE_STRING;
123}
124
125
126void
127WriterImplBase::AttributeValue::SetToData(uint64 size, uint64 offset)
128{
129	data.size = size;
130	data.offset = offset;
131	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
132	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP;
133}
134
135
136void
137WriterImplBase::AttributeValue::SetToData(uint64 size, const void* rawData)
138{
139	data.size = size;
140	if (size > 0)
141		memcpy(data.raw, rawData, size);
142	type = B_HPKG_ATTRIBUTE_TYPE_RAW;
143	encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE;
144}
145
146
147uint8
148WriterImplBase::AttributeValue::ApplicableEncoding() const
149{
150	switch (type) {
151		case B_HPKG_ATTRIBUTE_TYPE_INT:
152			return _ApplicableIntEncoding(signedInt >= 0
153				? (uint64)signedInt << 1
154				: (uint64)(-(signedInt + 1) << 1));
155		case B_HPKG_ATTRIBUTE_TYPE_UINT:
156			return _ApplicableIntEncoding(unsignedInt);
157		case B_HPKG_ATTRIBUTE_TYPE_STRING:
158			return string->index >= 0
159				? B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE
160				: B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE;
161		case B_HPKG_ATTRIBUTE_TYPE_RAW:
162			return encoding;
163		default:
164			return 0;
165	}
166}
167
168
169/*static*/ uint8
170WriterImplBase::AttributeValue::_ApplicableIntEncoding(uint64 value)
171{
172	if (value <= 0xff)
173		return B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT;
174	if (value <= 0xffff)
175		return B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT;
176	if (value <= 0xffffffff)
177		return B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT;
178
179	return B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT;
180}
181
182
183// #pragma mark - PackageAttribute
184
185
186WriterImplBase::PackageAttribute::PackageAttribute(BHPKGAttributeID id_,
187	uint8 type_, uint8 encoding_)
188	:
189	id(id_)
190{
191	type = type_;
192	encoding = encoding_;
193}
194
195
196WriterImplBase::PackageAttribute::~PackageAttribute()
197{
198	_DeleteChildren();
199}
200
201
202void
203WriterImplBase::PackageAttribute::AddChild(PackageAttribute* child)
204{
205	children.Add(child);
206}
207
208
209void
210WriterImplBase::PackageAttribute::_DeleteChildren()
211{
212	while (PackageAttribute* child = children.RemoveHead())
213		delete child;
214}
215
216
217// #pragma mark - WriterImplBase
218
219
220WriterImplBase::WriterImplBase(const char* fileType, BErrorOutput* errorOutput)
221	:
222	fHeapWriter(NULL),
223	fCompressionAlgorithm(NULL),
224	fCompressionParameters(NULL),
225	fDecompressionAlgorithm(NULL),
226	fDecompressionParameters(NULL),
227	fFileType(fileType),
228	fErrorOutput(errorOutput),
229	fFileName(NULL),
230	fParameters(),
231	fFile(NULL),
232	fOwnsFile(false),
233	fFinished(false)
234{
235}
236
237
238WriterImplBase::~WriterImplBase()
239{
240	delete fHeapWriter;
241	delete fCompressionAlgorithm;
242	delete fCompressionParameters;
243	delete fDecompressionAlgorithm;
244	delete fDecompressionParameters;
245
246	if (fOwnsFile)
247		delete fFile;
248
249	if (!fFinished && fFileName != NULL
250		&& (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0) {
251		unlink(fFileName);
252	}
253}
254
255
256status_t
257WriterImplBase::Init(BPositionIO* file, bool keepFile, const char* fileName,
258	const BPackageWriterParameters& parameters)
259{
260	fParameters = parameters;
261
262	if (fPackageStringCache.Init() != B_OK)
263		throw std::bad_alloc();
264
265	if (file == NULL) {
266		if (fileName == NULL)
267			return B_BAD_VALUE;
268
269		// open file (don't truncate in update mode)
270		int openMode = O_RDWR;
271		if ((parameters.Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) == 0)
272			openMode |= O_CREAT | O_TRUNC;
273
274		BFile* newFile = new BFile;
275		status_t error = newFile->SetTo(fileName, openMode);
276		if (error != B_OK) {
277			fErrorOutput->PrintError("Failed to open %s file \"%s\": %s\n",
278				fFileType, fileName, strerror(errno));
279			delete newFile;
280			return error;
281		}
282
283		fFile = newFile;
284		fOwnsFile = true;
285	} else {
286		fFile = file;
287		fOwnsFile = keepFile;
288	}
289
290	fFileName = fileName;
291
292	return B_OK;
293}
294
295
296status_t
297WriterImplBase::InitHeapReader(size_t headerSize)
298{
299	// allocate the compression/decompression algorithm
300	CompressionAlgorithmOwner* compressionAlgorithm = NULL;
301	BReference<CompressionAlgorithmOwner> compressionAlgorithmReference;
302
303	DecompressionAlgorithmOwner* decompressionAlgorithm = NULL;
304	BReference<DecompressionAlgorithmOwner> decompressionAlgorithmReference;
305
306	switch (fParameters.Compression()) {
307		case B_HPKG_COMPRESSION_NONE:
308			break;
309		case B_HPKG_COMPRESSION_ZLIB:
310			compressionAlgorithm = CompressionAlgorithmOwner::Create(
311				new(std::nothrow) BZlibCompressionAlgorithm,
312				new(std::nothrow) BZlibCompressionParameters(
313					(fParameters.CompressionLevel() / float(B_HPKG_COMPRESSION_LEVEL_BEST))
314						* B_ZLIB_COMPRESSION_BEST));
315			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
316
317			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
318				new(std::nothrow) BZlibCompressionAlgorithm,
319				new(std::nothrow) BZlibDecompressionParameters);
320			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
321
322			if (compressionAlgorithm == NULL
323				|| compressionAlgorithm->algorithm == NULL
324				|| compressionAlgorithm->parameters == NULL
325				|| decompressionAlgorithm == NULL
326				|| decompressionAlgorithm->algorithm == NULL
327				|| decompressionAlgorithm->parameters == NULL) {
328				throw std::bad_alloc();
329			}
330			break;
331		case B_HPKG_COMPRESSION_ZSTD:
332			compressionAlgorithm = CompressionAlgorithmOwner::Create(
333				new(std::nothrow) BZstdCompressionAlgorithm,
334				new(std::nothrow) BZstdCompressionParameters(
335					(fParameters.CompressionLevel() / float(B_HPKG_COMPRESSION_LEVEL_BEST))
336						* B_ZSTD_COMPRESSION_BEST));
337			compressionAlgorithmReference.SetTo(compressionAlgorithm, true);
338
339			decompressionAlgorithm = DecompressionAlgorithmOwner::Create(
340				new(std::nothrow) BZstdCompressionAlgorithm,
341				new(std::nothrow) BZstdDecompressionParameters);
342			decompressionAlgorithmReference.SetTo(decompressionAlgorithm, true);
343
344			if (compressionAlgorithm == NULL
345				|| compressionAlgorithm->algorithm == NULL
346				|| compressionAlgorithm->parameters == NULL
347				|| decompressionAlgorithm == NULL
348				|| decompressionAlgorithm->algorithm == NULL
349				|| decompressionAlgorithm->parameters == NULL) {
350				throw std::bad_alloc();
351			}
352			break;
353		default:
354			fErrorOutput->PrintError("Error: Invalid heap compression\n");
355			return B_BAD_VALUE;
356	}
357
358	// create heap writer
359	fHeapWriter = new PackageFileHeapWriter(fErrorOutput, fFile, headerSize,
360		compressionAlgorithm, decompressionAlgorithm);
361	fHeapWriter->Init();
362
363	return B_OK;
364}
365
366
367void
368WriterImplBase::SetCompression(uint32 compression)
369{
370	fParameters.SetCompression(compression);
371}
372
373
374void
375WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList,
376	const BPackageInfo& packageInfo)
377{
378	// name
379	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_NAME, packageInfo.Name(),
380		attributeList);
381
382	// summary
383	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_SUMMARY,
384		packageInfo.Summary(), attributeList);
385
386	// description
387	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_DESCRIPTION,
388		packageInfo.Description(), attributeList);
389
390	// vendor
391	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VENDOR,
392		packageInfo.Vendor(), attributeList);
393
394	// packager
395	AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_PACKAGER,
396		packageInfo.Packager(), attributeList);
397
398	// base package (optional)
399	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_BASE_PACKAGE,
400		packageInfo.BasePackage(), attributeList);
401
402	// flags
403	PackageAttribute* flags = new PackageAttribute(
404		B_HPKG_ATTRIBUTE_ID_PACKAGE_FLAGS, B_HPKG_ATTRIBUTE_TYPE_UINT,
405		B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
406	flags->unsignedInt = packageInfo.Flags();
407	attributeList.Add(flags);
408
409	// architecture
410	PackageAttribute* architecture = new PackageAttribute(
411		B_HPKG_ATTRIBUTE_ID_PACKAGE_ARCHITECTURE, B_HPKG_ATTRIBUTE_TYPE_UINT,
412		B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
413	architecture->unsignedInt = packageInfo.Architecture();
414	attributeList.Add(architecture);
415
416	// version
417	RegisterPackageVersion(attributeList, packageInfo.Version());
418
419	// copyright list
420	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_COPYRIGHT,
421			packageInfo.CopyrightList(), attributeList);
422
423	// license list
424	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_LICENSE,
425		packageInfo.LicenseList(), attributeList);
426
427	// URL list
428	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_URL,
429		packageInfo.URLList(), attributeList);
430
431	// source URL list
432	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_SOURCE_URL,
433		packageInfo.SourceURLList(), attributeList);
434
435	// provides list
436	const BObjectList<BPackageResolvable>& providesList
437		= packageInfo.ProvidesList();
438	for (int i = 0; i < providesList.CountItems(); ++i) {
439		BPackageResolvable* resolvable = providesList.ItemAt(i);
440		bool hasVersion = resolvable->Version().InitCheck() == B_OK;
441		bool hasCompatibleVersion
442			= resolvable->CompatibleVersion().InitCheck() == B_OK;
443
444		PackageAttribute* provides = AddStringAttribute(
445			B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES, resolvable->Name(),
446			attributeList);
447
448		if (hasVersion)
449			RegisterPackageVersion(provides->children, resolvable->Version());
450
451		if (hasCompatibleVersion) {
452			RegisterPackageVersion(provides->children,
453				resolvable->CompatibleVersion(),
454				B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES_COMPATIBLE);
455		}
456	}
457
458	// requires list
459	RegisterPackageResolvableExpressionList(attributeList,
460		packageInfo.RequiresList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_REQUIRES);
461
462	// supplements list
463	RegisterPackageResolvableExpressionList(attributeList,
464		packageInfo.SupplementsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_SUPPLEMENTS);
465
466	// conflicts list
467	RegisterPackageResolvableExpressionList(attributeList,
468		packageInfo.ConflictsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_CONFLICTS);
469
470	// freshens list
471	RegisterPackageResolvableExpressionList(attributeList,
472		packageInfo.FreshensList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_FRESHENS);
473
474	// replaces list
475	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES,
476		packageInfo.ReplacesList(), attributeList);
477
478	// global writable file info list
479	const BObjectList<BGlobalWritableFileInfo>& globalWritableFileInfos
480		= packageInfo.GlobalWritableFileInfos();
481	for (int32 i = 0; i < globalWritableFileInfos.CountItems(); ++i) {
482		BGlobalWritableFileInfo* info = globalWritableFileInfos.ItemAt(i);
483		PackageAttribute* attribute = AddStringAttribute(
484			B_HPKG_ATTRIBUTE_ID_PACKAGE_GLOBAL_WRITABLE_FILE, info->Path(),
485			attributeList);
486
487		if (info->IsDirectory()) {
488			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
489				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
490				B_HPKG_ATTRIBUTE_TYPE_UINT,
491				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
492			isDirectoryAttribute->unsignedInt = 1;
493			attribute->children.Add(isDirectoryAttribute);
494		}
495
496		if (info->IsIncluded()) {
497			PackageAttribute* updateTypeAttribute = new PackageAttribute(
498				B_HPKG_ATTRIBUTE_ID_PACKAGE_WRITABLE_FILE_UPDATE_TYPE,
499				B_HPKG_ATTRIBUTE_TYPE_UINT,
500				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
501			updateTypeAttribute->unsignedInt = info->UpdateType();
502			attribute->children.Add(updateTypeAttribute);
503		}
504	}
505
506	// user settings file info list
507	const BObjectList<BUserSettingsFileInfo>& userSettingsFileInfos
508		= packageInfo.UserSettingsFileInfos();
509	for (int32 i = 0; i < userSettingsFileInfos.CountItems(); ++i) {
510		BUserSettingsFileInfo* info = userSettingsFileInfos.ItemAt(i);
511		PackageAttribute* attribute = AddStringAttribute(
512			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SETTINGS_FILE, info->Path(),
513			attributeList);
514
515		if (info->IsDirectory()) {
516			PackageAttribute* isDirectoryAttribute = new PackageAttribute(
517				B_HPKG_ATTRIBUTE_ID_PACKAGE_IS_WRITABLE_DIRECTORY,
518				B_HPKG_ATTRIBUTE_TYPE_UINT,
519				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
520			isDirectoryAttribute->unsignedInt = 1;
521			attribute->children.Add(isDirectoryAttribute);
522		} else {
523			_AddStringAttributeIfNotEmpty(
524				B_HPKG_ATTRIBUTE_ID_PACKAGE_SETTINGS_FILE_TEMPLATE,
525				info->TemplatePath(), attribute->children);
526		}
527	}
528
529	// user list
530	const BObjectList<BUser>& users = packageInfo.Users();
531	for (int32 i = 0; i < users.CountItems(); ++i) {
532		const BUser* user = users.ItemAt(i);
533		PackageAttribute* attribute = AddStringAttribute(
534			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER, user->Name(), attributeList);
535
536		_AddStringAttributeIfNotEmpty(
537			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_REAL_NAME, user->RealName(),
538			attribute->children);
539		_AddStringAttributeIfNotEmpty(
540			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_HOME, user->Home(),
541			attribute->children);
542		_AddStringAttributeIfNotEmpty(
543			B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_SHELL, user->Shell(),
544			attribute->children);
545
546		for (int32 k = 0; k < user->Groups().CountStrings(); k++) {
547			AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_USER_GROUP,
548				user->Groups().StringAt(k), attribute->children);
549		}
550	}
551
552	// group list
553	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_GROUP,
554		packageInfo.Groups(), attributeList);
555
556	// post install script list
557	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_POST_INSTALL_SCRIPT,
558		packageInfo.PostInstallScripts(), attributeList);
559
560	// pre uninstall script list
561	_AddStringAttributeList(B_HPKG_ATTRIBUTE_ID_PACKAGE_PRE_UNINSTALL_SCRIPT,
562		packageInfo.PreUninstallScripts(), attributeList);
563
564	// checksum (optional, only exists in repositories)
565	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM,
566		packageInfo.Checksum(), attributeList);
567
568	// install path (optional)
569	_AddStringAttributeIfNotEmpty(B_HPKG_ATTRIBUTE_ID_PACKAGE_INSTALL_PATH,
570		packageInfo.InstallPath(), attributeList);
571}
572
573
574void
575WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
576	const BPackageVersion& version, BHPKGAttributeID attributeID)
577{
578	PackageAttribute* versionMajor = AddStringAttribute(attributeID,
579		version.Major(), attributeList);
580
581	if (!version.Minor().IsEmpty()) {
582		AddStringAttribute(B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MINOR,
583			version.Minor(), versionMajor->children);
584		_AddStringAttributeIfNotEmpty(
585			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MICRO, version.Micro(),
586			versionMajor->children);
587	}
588
589	_AddStringAttributeIfNotEmpty(
590		B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
591		version.PreRelease(), versionMajor->children);
592
593	if (version.Revision() != 0) {
594		PackageAttribute* versionRevision = new PackageAttribute(
595			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_REVISION,
596			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
597		versionRevision->unsignedInt = version.Revision();
598		versionMajor->children.Add(versionRevision);
599	}
600}
601
602
603void
604WriterImplBase::RegisterPackageResolvableExpressionList(
605	PackageAttributeList& attributeList,
606	const BObjectList<BPackageResolvableExpression>& expressionList, uint8 id)
607{
608	for (int i = 0; i < expressionList.CountItems(); ++i) {
609		BPackageResolvableExpression* resolvableExpr = expressionList.ItemAt(i);
610		PackageAttribute* name = AddStringAttribute((BHPKGAttributeID)id,
611			resolvableExpr->Name(), attributeList);
612
613		if (resolvableExpr->Version().InitCheck() == B_OK) {
614			PackageAttribute* op = new PackageAttribute(
615				B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR,
616				B_HPKG_ATTRIBUTE_TYPE_UINT,
617				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
618			op->unsignedInt = resolvableExpr->Operator();
619			name->children.Add(op);
620			RegisterPackageVersion(name->children, resolvableExpr->Version());
621		}
622	}
623}
624
625
626WriterImplBase::PackageAttribute*
627WriterImplBase::AddStringAttribute(BHPKGAttributeID id, const BString& value,
628	DoublyLinkedList<PackageAttribute>& list)
629{
630	PackageAttribute* attribute = new PackageAttribute(id,
631		B_HPKG_ATTRIBUTE_TYPE_STRING, B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
632	attribute->string = fPackageStringCache.Get(value);
633	list.Add(attribute);
634	return attribute;
635}
636
637
638int32
639WriterImplBase::WriteCachedStrings(const StringCache& cache,
640	uint32 minUsageCount)
641{
642	// create an array of the cached strings
643	int32 count = cache.CountElements();
644	CachedString** cachedStrings = new CachedString*[count];
645	ArrayDeleter<CachedString*> cachedStringsDeleter(cachedStrings);
646
647	int32 index = 0;
648	for (CachedStringTable::Iterator it = cache.GetIterator();
649			CachedString* string = it.Next();) {
650		cachedStrings[index++] = string;
651	}
652
653	// sort it by descending usage count
654	std::sort(cachedStrings, cachedStrings + count, CachedStringUsageGreater());
655
656	// assign the indices and write entries to disk
657	int32 stringsWritten = 0;
658	for (int32 i = 0; i < count; i++) {
659		CachedString* cachedString = cachedStrings[i];
660
661		// empty strings must be stored inline, as they can't be distinguished
662		// from the end-marker!
663		if (strlen(cachedString->string) == 0)
664			continue;
665
666		// strings that are used only once are better stored inline
667		if (cachedString->usageCount < minUsageCount)
668			break;
669
670		WriteString(cachedString->string);
671
672		cachedString->index = stringsWritten++;
673	}
674
675	// write a terminating 0 byte
676	Write<uint8>(0);
677
678	return stringsWritten;
679}
680
681
682int32
683WriterImplBase::WritePackageAttributes(
684	const PackageAttributeList& packageAttributes,
685	uint32& _stringsLengthUncompressed)
686{
687	// write the cached strings
688	uint64 startOffset = fHeapWriter->UncompressedHeapSize();
689	uint32 stringsCount = WriteCachedStrings(fPackageStringCache, 2);
690	_stringsLengthUncompressed
691		= fHeapWriter->UncompressedHeapSize() - startOffset;
692
693	_WritePackageAttributes(packageAttributes);
694
695	return stringsCount;
696}
697
698
699void
700WriterImplBase::WriteAttributeValue(const AttributeValue& value, uint8 encoding)
701{
702	switch (value.type) {
703		case B_HPKG_ATTRIBUTE_TYPE_INT:
704		case B_HPKG_ATTRIBUTE_TYPE_UINT:
705		{
706			uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT
707				? (uint64)value.signedInt : value.unsignedInt;
708
709			switch (encoding) {
710				case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
711					Write<uint8>((uint8)intValue);
712					break;
713				case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
714					Write<uint16>(
715						B_HOST_TO_BENDIAN_INT16((uint16)intValue));
716					break;
717				case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
718					Write<uint32>(
719						B_HOST_TO_BENDIAN_INT32((uint32)intValue));
720					break;
721				case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
722					Write<uint64>(
723						B_HOST_TO_BENDIAN_INT64((uint64)intValue));
724					break;
725				default:
726				{
727					fErrorOutput->PrintError("WriteAttributeValue(): invalid "
728						"encoding %d for int value type %d\n", encoding,
729						value.type);
730					throw status_t(B_BAD_VALUE);
731				}
732			}
733
734			break;
735		}
736
737		case B_HPKG_ATTRIBUTE_TYPE_STRING:
738		{
739			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE)
740				WriteUnsignedLEB128(value.string->index);
741			else
742				WriteString(value.string->string);
743			break;
744		}
745
746		case B_HPKG_ATTRIBUTE_TYPE_RAW:
747		{
748			WriteUnsignedLEB128(value.data.size);
749			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP)
750				WriteUnsignedLEB128(value.data.offset);
751			else
752				fHeapWriter->AddDataThrows(value.data.raw, value.data.size);
753			break;
754		}
755
756		default:
757			fErrorOutput->PrintError(
758				"WriteAttributeValue(): invalid value type: %d\n", value.type);
759			throw status_t(B_BAD_VALUE);
760	}
761}
762
763
764void
765WriterImplBase::WriteUnsignedLEB128(uint64 value)
766{
767	uint8 bytes[10];
768	int32 count = 0;
769	do {
770		uint8 byte = value & 0x7f;
771		value >>= 7;
772		bytes[count++] = byte | (value != 0 ? 0x80 : 0);
773	} while (value != 0);
774
775	fHeapWriter->AddDataThrows(bytes, count);
776}
777
778
779void
780WriterImplBase::RawWriteBuffer(const void* buffer, size_t size, off_t offset)
781{
782	status_t error = fFile->WriteAtExactly(offset, buffer, size);
783	if (error != B_OK) {
784		fErrorOutput->PrintError(
785			"RawWriteBuffer(%p, %lu) failed to write data: %s\n", buffer, size,
786			strerror(error));
787		throw error;
788	}
789}
790
791
792void
793WriterImplBase::_AddStringAttributeList(BHPKGAttributeID id,
794	const BStringList& value, DoublyLinkedList<PackageAttribute>& list)
795{
796	for (int32 i = 0; i < value.CountStrings(); i++)
797		AddStringAttribute(id, value.StringAt(i), list);
798}
799
800
801void
802WriterImplBase::_WritePackageAttributes(
803	const PackageAttributeList& packageAttributes)
804{
805	DoublyLinkedList<PackageAttribute>::ConstIterator it
806		= packageAttributes.GetIterator();
807	while (PackageAttribute* attribute = it.Next()) {
808		uint8 encoding = attribute->ApplicableEncoding();
809
810		// write tag
811		WriteUnsignedLEB128(compose_attribute_tag(
812			attribute->id, attribute->type, encoding,
813			!attribute->children.IsEmpty()));
814
815		// write value
816		WriteAttributeValue(*attribute, encoding);
817
818		if (!attribute->children.IsEmpty())
819			_WritePackageAttributes(attribute->children);
820	}
821
822	WriteUnsignedLEB128(0);
823}
824
825
826}	// namespace BPrivate
827
828}	// namespace BHPKG
829
830}	// namespace BPackageKit
831