WriterImplBase.cpp revision 4dcc52327b52d043011c084e4ae8894b1f2d1481
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/haiku_package.h>
22
23#include <package/hpkg/DataReader.h>
24#include <package/hpkg/ErrorOutput.h>
25
26
27namespace BPackageKit {
28
29namespace BHPKG {
30
31namespace BPrivate {
32
33
34// #pragma mark - AttributeValue
35
36
37WriterImplBase::AttributeValue::AttributeValue()
38	:
39	type(B_HPKG_ATTRIBUTE_TYPE_INVALID),
40	encoding(-1)
41{
42}
43
44
45WriterImplBase::AttributeValue::~AttributeValue()
46{
47}
48
49
50void
51WriterImplBase::AttributeValue::SetTo(int8 value)
52{
53	signedInt = value;
54	type = B_HPKG_ATTRIBUTE_TYPE_INT;
55}
56
57
58void
59WriterImplBase::AttributeValue::SetTo(uint8 value)
60{
61	unsignedInt = value;
62	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
63}
64
65
66void
67WriterImplBase::AttributeValue::SetTo(int16 value)
68{
69	signedInt = value;
70	type = B_HPKG_ATTRIBUTE_TYPE_INT;
71}
72
73
74void
75WriterImplBase::AttributeValue::SetTo(uint16 value)
76{
77	unsignedInt = value;
78	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
79}
80
81
82void
83WriterImplBase::AttributeValue::SetTo(int32 value)
84{
85	signedInt = value;
86	type = B_HPKG_ATTRIBUTE_TYPE_INT;
87}
88
89
90void
91WriterImplBase::AttributeValue::SetTo(uint32 value)
92{
93	unsignedInt = value;
94	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
95}
96
97
98void
99WriterImplBase::AttributeValue::SetTo(int64 value)
100{
101	signedInt = value;
102	type = B_HPKG_ATTRIBUTE_TYPE_INT;
103}
104
105
106void
107WriterImplBase::AttributeValue::SetTo(uint64 value)
108{
109	unsignedInt = value;
110	type = B_HPKG_ATTRIBUTE_TYPE_UINT;
111}
112
113
114void
115WriterImplBase::AttributeValue::SetTo(CachedString* value)
116{
117	string = value;
118	type = B_HPKG_ATTRIBUTE_TYPE_STRING;
119}
120
121
122void
123WriterImplBase::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
132void
133WriterImplBase::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
143uint8
144WriterImplBase::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
166WriterImplBase::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
182WriterImplBase::AbstractDataWriter::AbstractDataWriter()
183	:
184	fBytesWritten(0)
185{
186}
187
188
189WriterImplBase::AbstractDataWriter::~AbstractDataWriter()
190{
191}
192
193
194// #pragma mark - FDDataWriter
195
196
197WriterImplBase::FDDataWriter::FDDataWriter(int fd, off_t offset,
198	BErrorOutput* errorOutput)
199	:
200	fFD(fd),
201	fOffset(offset),
202	fErrorOutput(errorOutput)
203{
204}
205
206
207status_t
208WriterImplBase::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
233WriterImplBase::ZlibDataWriter::ZlibDataWriter(AbstractDataWriter* dataWriter)
234	:
235	fDataWriter(dataWriter),
236	fCompressor(this)
237{
238}
239
240
241void
242WriterImplBase::ZlibDataWriter::Init()
243{
244	status_t error = fCompressor.Init();
245	if (error != B_OK)
246		throw status_t(error);
247}
248
249
250void
251WriterImplBase::ZlibDataWriter::Finish()
252{
253	status_t error = fCompressor.Finish();
254	if (error != B_OK)
255		throw status_t(error);
256}
257
258
259status_t
260WriterImplBase::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
270status_t
271WriterImplBase::ZlibDataWriter::WriteData(const void* buffer, size_t size)
272{
273	return fDataWriter->WriteDataNoThrow(buffer, size);
274}
275
276
277// #pragma mark - PackageAttribute
278
279WriterImplBase::PackageAttribute::PackageAttribute(HPKGPackageAttributeID id_,
280	uint8 type_, uint8 encoding_)
281	:
282	id(id_)
283{
284	type = type_;
285	encoding = encoding_;
286}
287
288
289WriterImplBase::PackageAttribute::~PackageAttribute()
290{
291	_DeleteChildren();
292}
293
294
295void
296WriterImplBase::PackageAttribute::AddChild(PackageAttribute* child)
297{
298	children.Add(child);
299}
300
301
302void
303WriterImplBase::PackageAttribute::_DeleteChildren()
304{
305	while (PackageAttribute* child = children.RemoveHead())
306		delete child;
307}
308
309
310// #pragma mark - WriterImplBase
311
312
313WriterImplBase::WriterImplBase(BErrorOutput* errorOutput)
314	:
315	fErrorOutput(errorOutput),
316	fFileName(NULL),
317	fFD(-1),
318	fFinished(false),
319	fDataWriter(NULL)
320{
321}
322
323
324WriterImplBase::~WriterImplBase()
325{
326	if (fFD >= 0)
327		close(fFD);
328
329	if (!fFinished && fFileName != NULL)
330		unlink(fFileName);
331}
332
333
334status_t
335WriterImplBase::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
355void
356WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList,
357	const BPackageInfo& packageInfo)
358{
359	// name
360	PackageAttribute* name = new PackageAttribute(HPKG_PACKAGE_ATTRIBUTE_NAME,
361		B_HPKG_ATTRIBUTE_TYPE_STRING, B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
362	name->string = fPackageStringCache.Get(packageInfo.Name().String());
363	attributeList.Add(name);
364
365	// summary
366	PackageAttribute* summary = new PackageAttribute(
367		HPKG_PACKAGE_ATTRIBUTE_SUMMARY, B_HPKG_ATTRIBUTE_TYPE_STRING,
368		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
369	summary->string = fPackageStringCache.Get(packageInfo.Summary().String());
370	attributeList.Add(summary);
371
372	// description
373	PackageAttribute* description = new PackageAttribute(
374		HPKG_PACKAGE_ATTRIBUTE_DESCRIPTION, B_HPKG_ATTRIBUTE_TYPE_STRING,
375		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
376	description->string
377		= fPackageStringCache.Get(packageInfo.Description().String());
378	attributeList.Add(description);
379
380	// vendor
381	PackageAttribute* vendor = new PackageAttribute(
382		HPKG_PACKAGE_ATTRIBUTE_VENDOR, B_HPKG_ATTRIBUTE_TYPE_STRING,
383		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
384	vendor->string = fPackageStringCache.Get(packageInfo.Vendor().String());
385	attributeList.Add(vendor);
386
387	// packager
388	PackageAttribute* packager = new PackageAttribute(
389		HPKG_PACKAGE_ATTRIBUTE_PACKAGER, B_HPKG_ATTRIBUTE_TYPE_STRING,
390		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
391	packager->string = fPackageStringCache.Get(packageInfo.Packager().String());
392	attributeList.Add(packager);
393
394	// flags
395	PackageAttribute* flags = new PackageAttribute(
396		HPKG_PACKAGE_ATTRIBUTE_FLAGS, B_HPKG_ATTRIBUTE_TYPE_UINT,
397		B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
398	flags->unsignedInt = packageInfo.Flags();
399	attributeList.Add(flags);
400
401	// architecture
402	PackageAttribute* architecture = new PackageAttribute(
403		HPKG_PACKAGE_ATTRIBUTE_ARCHITECTURE, B_HPKG_ATTRIBUTE_TYPE_UINT,
404		B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
405	architecture->unsignedInt = packageInfo.Architecture();
406	attributeList.Add(architecture);
407
408	// version
409	RegisterPackageVersion(attributeList, packageInfo.Version());
410
411	// copyright list
412	const BObjectList<BString>& copyrightList = packageInfo.CopyrightList();
413	for (int i = 0; i < copyrightList.CountItems(); ++i) {
414		PackageAttribute* copyright = new PackageAttribute(
415			HPKG_PACKAGE_ATTRIBUTE_COPYRIGHT, B_HPKG_ATTRIBUTE_TYPE_STRING,
416			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
417		copyright->string
418			= fPackageStringCache.Get(copyrightList.ItemAt(i)->String());
419		attributeList.Add(copyright);
420	}
421
422	// license list
423	const BObjectList<BString>& licenseList = packageInfo.LicenseList();
424	for (int i = 0; i < licenseList.CountItems(); ++i) {
425		PackageAttribute* license = new PackageAttribute(
426			HPKG_PACKAGE_ATTRIBUTE_LICENSE, B_HPKG_ATTRIBUTE_TYPE_STRING,
427			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
428		license->string
429			= fPackageStringCache.Get(licenseList.ItemAt(i)->String());
430		attributeList.Add(license);
431	}
432
433	// provides list
434	const BObjectList<BPackageResolvable>& providesList
435		= packageInfo.ProvidesList();
436	for (int i = 0; i < providesList.CountItems(); ++i) {
437		BPackageResolvable* resolvable = providesList.ItemAt(i);
438		bool hasVersion = resolvable->Version().InitCheck() == B_OK;
439
440		PackageAttribute* providesType = new PackageAttribute(
441			HPKG_PACKAGE_ATTRIBUTE_PROVIDES_TYPE, B_HPKG_ATTRIBUTE_TYPE_UINT,
442			B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
443		providesType->unsignedInt = resolvable->Type();
444		attributeList.Add(providesType);
445
446		PackageAttribute* provides = new PackageAttribute(
447			HPKG_PACKAGE_ATTRIBUTE_PROVIDES, B_HPKG_ATTRIBUTE_TYPE_STRING,
448			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
449		provides->string = fPackageStringCache.Get(resolvable->Name().String());
450		attributeList.Add(provides);
451
452		if (hasVersion)
453			RegisterPackageVersion(provides->children, resolvable->Version());
454	}
455
456	// requires list
457	RegisterPackageResolvableExpressionList(attributeList,
458		packageInfo.RequiresList(), HPKG_PACKAGE_ATTRIBUTE_REQUIRES);
459
460	// supplements list
461	RegisterPackageResolvableExpressionList(attributeList,
462		packageInfo.SupplementsList(), HPKG_PACKAGE_ATTRIBUTE_SUPPLEMENTS);
463
464	// conflicts list
465	RegisterPackageResolvableExpressionList(attributeList,
466		packageInfo.ConflictsList(), HPKG_PACKAGE_ATTRIBUTE_CONFLICTS);
467
468	// freshens list
469	RegisterPackageResolvableExpressionList(attributeList,
470		packageInfo.FreshensList(), HPKG_PACKAGE_ATTRIBUTE_FRESHENS);
471
472	// replaces list
473	const BObjectList<BString>& replacesList = packageInfo.ReplacesList();
474	for (int i = 0; i < replacesList.CountItems(); ++i) {
475		PackageAttribute* replaces = new PackageAttribute(
476			HPKG_PACKAGE_ATTRIBUTE_REPLACES, B_HPKG_ATTRIBUTE_TYPE_STRING,
477			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
478		replaces->string
479			= fPackageStringCache.Get(replacesList.ItemAt(i)->String());
480		attributeList.Add(replaces);
481	}
482
483	// checksum (optional, only exists in repositories)
484	if (packageInfo.Checksum().Length() > 0) {
485		PackageAttribute* checksum = new PackageAttribute(
486			HPKG_PACKAGE_ATTRIBUTE_CHECKSUM, B_HPKG_ATTRIBUTE_TYPE_STRING,
487			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
488		checksum->string
489			= fPackageStringCache.Get(packageInfo.Checksum().String());
490		attributeList.Add(checksum);
491	}
492}
493
494
495void
496WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
497	const BPackageVersion& version)
498{
499	PackageAttribute* versionMajor = new PackageAttribute(
500		HPKG_PACKAGE_ATTRIBUTE_VERSION_MAJOR, B_HPKG_ATTRIBUTE_TYPE_STRING,
501		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
502	versionMajor->string = fPackageStringCache.Get(version.Major().String());
503	attributeList.Add(versionMajor);
504
505	PackageAttribute* versionMinor = new PackageAttribute(
506		HPKG_PACKAGE_ATTRIBUTE_VERSION_MINOR, B_HPKG_ATTRIBUTE_TYPE_STRING,
507		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
508	versionMinor->string = fPackageStringCache.Get(version.Minor().String());
509	attributeList.Add(versionMinor);
510
511	PackageAttribute* versionMicro = new PackageAttribute(
512		HPKG_PACKAGE_ATTRIBUTE_VERSION_MICRO, B_HPKG_ATTRIBUTE_TYPE_STRING,
513		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
514	versionMicro->string = fPackageStringCache.Get(version.Micro().String());
515	attributeList.Add(versionMicro);
516
517	PackageAttribute* versionRelease = new PackageAttribute(
518		HPKG_PACKAGE_ATTRIBUTE_VERSION_RELEASE, B_HPKG_ATTRIBUTE_TYPE_UINT,
519		B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
520	versionRelease->unsignedInt = version.Release();
521	attributeList.Add(versionRelease);
522}
523
524
525void
526WriterImplBase::RegisterPackageResolvableExpressionList(
527	PackageAttributeList& attributeList,
528	const BObjectList<BPackageResolvableExpression>& expressionList, uint8 id)
529{
530	for (int i = 0; i < expressionList.CountItems(); ++i) {
531		BPackageResolvableExpression* resolvableExpr = expressionList.ItemAt(i);
532		bool hasVersion = resolvableExpr->Version().InitCheck() == B_OK;
533
534		PackageAttribute* name = new PackageAttribute(
535			(HPKGPackageAttributeID)id, B_HPKG_ATTRIBUTE_TYPE_STRING,
536			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
537		name->string = fPackageStringCache.Get(resolvableExpr->Name().String());
538		attributeList.Add(name);
539
540		if (hasVersion) {
541			PackageAttribute* op = new PackageAttribute(
542				HPKG_PACKAGE_ATTRIBUTE_RESOLVABLE_OPERATOR,
543				B_HPKG_ATTRIBUTE_TYPE_UINT,
544				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
545			op->unsignedInt = resolvableExpr->Operator();
546			name->children.Add(op);
547			RegisterPackageVersion(name->children, resolvableExpr->Version());
548		}
549	}
550}
551
552
553int32
554WriterImplBase::WriteCachedStrings(const StringCache& cache,
555	uint32 minUsageCount)
556{
557	// create an array of the cached strings
558	int32 count = cache.CountElements();
559	CachedString** cachedStrings = new CachedString*[count];
560	ArrayDeleter<CachedString*> cachedStringsDeleter(cachedStrings);
561
562	int32 index = 0;
563	for (CachedStringTable::Iterator it = cache.GetIterator();
564			CachedString* string = it.Next();) {
565		cachedStrings[index++] = string;
566	}
567
568	// sort it by descending usage count
569	std::sort(cachedStrings, cachedStrings + count, CachedStringUsageGreater());
570
571	// assign the indices and write entries to disk
572	int32 stringsWritten = 0;
573	for (int32 i = 0; i < count; i++) {
574		CachedString* cachedString = cachedStrings[i];
575
576		// empty strings must be stored inline, as they can't be distinguished
577		// from the end-marker!
578		if (strlen(cachedString->string) == 0)
579			continue;
580
581		// strings that are used only once are better stored inline
582		if (cachedString->usageCount < minUsageCount)
583			break;
584
585		WriteString(cachedString->string);
586
587		cachedString->index = stringsWritten++;
588	}
589
590	// write a terminating 0 byte
591	Write<uint8>(0);
592
593	return stringsWritten;
594}
595
596
597int32
598WriterImplBase::WritePackageAttributes(
599	const PackageAttributeList& packageAttributes,
600	uint32& _stringsLengthUncompressed)
601{
602	// write the cached strings
603	uint32 stringsCount = WriteCachedStrings(fPackageStringCache, 2);
604	_stringsLengthUncompressed = DataWriter()->BytesWritten();
605
606	_WritePackageAttributes(packageAttributes);
607
608	return stringsCount;
609}
610
611
612void
613WriterImplBase::WriteAttributeValue(const AttributeValue& value, uint8 encoding)
614{
615	switch (value.type) {
616		case B_HPKG_ATTRIBUTE_TYPE_INT:
617		case B_HPKG_ATTRIBUTE_TYPE_UINT:
618		{
619			uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT
620				? (uint64)value.signedInt : value.unsignedInt;
621
622			switch (encoding) {
623				case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
624					Write<uint8>((uint8)intValue);
625					break;
626				case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
627					Write<uint16>(
628						B_HOST_TO_BENDIAN_INT16((uint16)intValue));
629					break;
630				case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
631					Write<uint32>(
632						B_HOST_TO_BENDIAN_INT32((uint32)intValue));
633					break;
634				case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
635					Write<uint64>(
636						B_HOST_TO_BENDIAN_INT64((uint64)intValue));
637					break;
638				default:
639				{
640					fErrorOutput->PrintError("WriteAttributeValue(): invalid "
641						"encoding %d for int value type %d\n", encoding,
642						value.type);
643					throw status_t(B_BAD_VALUE);
644				}
645			}
646
647			break;
648		}
649
650		case B_HPKG_ATTRIBUTE_TYPE_STRING:
651		{
652			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE)
653				WriteUnsignedLEB128(value.string->index);
654			else
655				WriteString(value.string->string);
656			break;
657		}
658
659		case B_HPKG_ATTRIBUTE_TYPE_RAW:
660		{
661			WriteUnsignedLEB128(value.data.size);
662			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP)
663				WriteUnsignedLEB128(value.data.offset);
664			else
665				fDataWriter->WriteDataThrows(value.data.raw, value.data.size);
666			break;
667		}
668
669		default:
670			fErrorOutput->PrintError(
671				"WriteAttributeValue(): invalid value type: %d\n", value.type);
672			throw status_t(B_BAD_VALUE);
673	}
674}
675
676
677void
678WriterImplBase::WriteUnsignedLEB128(uint64 value)
679{
680	uint8 bytes[10];
681	int32 count = 0;
682	do {
683		uint8 byte = value & 0x7f;
684		value >>= 7;
685		bytes[count++] = byte | (value != 0 ? 0x80 : 0);
686	} while (value != 0);
687
688	fDataWriter->WriteDataThrows(bytes, count);
689}
690
691
692void
693WriterImplBase::WriteBuffer(const void* buffer, size_t size, off_t offset)
694{
695	ssize_t bytesWritten = pwrite(fFD, buffer, size, offset);
696	if (bytesWritten < 0) {
697		fErrorOutput->PrintError(
698			"WriteBuffer(%p, %lu) failed to write data: %s\n", buffer, size,
699			strerror(errno));
700		throw status_t(errno);
701	}
702	if ((size_t)bytesWritten != size) {
703		fErrorOutput->PrintError(
704			"WriteBuffer(%p, %lu) failed to write all data\n", buffer, size);
705		throw status_t(B_ERROR);
706	}
707}
708
709
710void
711WriterImplBase::_WritePackageAttributes(
712	const PackageAttributeList& packageAttributes)
713{
714	DoublyLinkedList<PackageAttribute>::ConstIterator it
715		= packageAttributes.GetIterator();
716	while (PackageAttribute* attribute = it.Next()) {
717		uint8 encoding = attribute->ApplicableEncoding();
718
719		// write tag
720		WriteUnsignedLEB128(HPKG_PACKAGE_ATTRIBUTE_TAG_COMPOSE(
721			attribute->id, attribute->type, encoding,
722			!attribute->children.IsEmpty()));
723
724		// write value
725		WriteAttributeValue(*attribute, encoding);
726
727		if (!attribute->children.IsEmpty())
728			_WritePackageAttributes(attribute->children);
729	}
730
731	WriteUnsignedLEB128(0);
732}
733
734
735}	// namespace BPrivate
736
737}	// namespace BHPKG
738
739}	// namespace BPackageKit
740