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