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