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
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(BHPKGAttributeID 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	fFlags(0),
318	fFD(-1),
319	fFinished(false),
320	fDataWriter(NULL)
321{
322}
323
324
325WriterImplBase::~WriterImplBase()
326{
327	if (fFD >= 0)
328		close(fFD);
329
330	if (!fFinished && fFileName != NULL
331		&& (fFlags & B_HPKG_WRITER_UPDATE_PACKAGE) == 0) {
332		unlink(fFileName);
333	}
334}
335
336
337status_t
338WriterImplBase::Init(const char* fileName, const char* type, uint32 flags)
339{
340	if (fPackageStringCache.Init() != B_OK)
341		throw std::bad_alloc();
342
343	// open file (don't truncate in update mode)
344	int openMode = O_RDWR;
345	if ((flags & B_HPKG_WRITER_UPDATE_PACKAGE) == 0)
346		openMode |= O_CREAT | O_TRUNC;
347
348	fFD = open(fileName, openMode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
349	if (fFD < 0) {
350		fErrorOutput->PrintError("Failed to open %s file \"%s\": %s\n", type,
351			fileName, strerror(errno));
352		return errno;
353	}
354
355	fFileName = fileName;
356	fFlags = flags;
357
358	return B_OK;
359}
360
361
362void
363WriterImplBase::RegisterPackageInfo(PackageAttributeList& attributeList,
364	const BPackageInfo& packageInfo)
365{
366	// name
367	PackageAttribute* name = new PackageAttribute(
368		B_HPKG_ATTRIBUTE_ID_PACKAGE_NAME, B_HPKG_ATTRIBUTE_TYPE_STRING,
369		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
370	name->string = fPackageStringCache.Get(packageInfo.Name().String());
371	attributeList.Add(name);
372
373	// summary
374	PackageAttribute* summary = new PackageAttribute(
375		B_HPKG_ATTRIBUTE_ID_PACKAGE_SUMMARY, B_HPKG_ATTRIBUTE_TYPE_STRING,
376		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
377	summary->string = fPackageStringCache.Get(packageInfo.Summary().String());
378	attributeList.Add(summary);
379
380	// description
381	PackageAttribute* description = new PackageAttribute(
382		B_HPKG_ATTRIBUTE_ID_PACKAGE_DESCRIPTION, B_HPKG_ATTRIBUTE_TYPE_STRING,
383		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
384	description->string
385		= fPackageStringCache.Get(packageInfo.Description().String());
386	attributeList.Add(description);
387
388	// vendor
389	PackageAttribute* vendor = new PackageAttribute(
390		B_HPKG_ATTRIBUTE_ID_PACKAGE_VENDOR, B_HPKG_ATTRIBUTE_TYPE_STRING,
391		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
392	vendor->string = fPackageStringCache.Get(packageInfo.Vendor().String());
393	attributeList.Add(vendor);
394
395	// packager
396	PackageAttribute* packager = new PackageAttribute(
397		B_HPKG_ATTRIBUTE_ID_PACKAGE_PACKAGER, B_HPKG_ATTRIBUTE_TYPE_STRING,
398		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
399	packager->string = fPackageStringCache.Get(packageInfo.Packager().String());
400	attributeList.Add(packager);
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	const BStringList& copyrightList = packageInfo.CopyrightList();
421	for (int i = 0; i < copyrightList.CountStrings(); ++i) {
422		PackageAttribute* copyright = new PackageAttribute(
423			B_HPKG_ATTRIBUTE_ID_PACKAGE_COPYRIGHT, B_HPKG_ATTRIBUTE_TYPE_STRING,
424			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
425		copyright->string = fPackageStringCache.Get(copyrightList.StringAt(i));
426		attributeList.Add(copyright);
427	}
428
429	// license list
430	const BStringList& licenseList = packageInfo.LicenseList();
431	for (int i = 0; i < licenseList.CountStrings(); ++i) {
432		PackageAttribute* license = new PackageAttribute(
433			B_HPKG_ATTRIBUTE_ID_PACKAGE_LICENSE, B_HPKG_ATTRIBUTE_TYPE_STRING,
434			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
435		license->string = fPackageStringCache.Get(licenseList.StringAt(i));
436		attributeList.Add(license);
437	}
438
439	// URL list
440	const BStringList& urlList = packageInfo.URLList();
441	for (int i = 0; i < urlList.CountStrings(); ++i) {
442		PackageAttribute* url = new PackageAttribute(
443			B_HPKG_ATTRIBUTE_ID_PACKAGE_URL, B_HPKG_ATTRIBUTE_TYPE_STRING,
444			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
445		url->string = fPackageStringCache.Get(urlList.StringAt(i));
446		attributeList.Add(url);
447	}
448
449	// source URL list
450	const BStringList& sourceURLList = packageInfo.SourceURLList();
451	for (int i = 0; i < sourceURLList.CountStrings(); ++i) {
452		PackageAttribute* url = new PackageAttribute(
453			B_HPKG_ATTRIBUTE_ID_PACKAGE_SOURCE_URL,
454			B_HPKG_ATTRIBUTE_TYPE_STRING,
455			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
456		url->string = fPackageStringCache.Get(sourceURLList.StringAt(i));
457		attributeList.Add(url);
458	}
459
460	// provides list
461	const BObjectList<BPackageResolvable>& providesList
462		= packageInfo.ProvidesList();
463	for (int i = 0; i < providesList.CountItems(); ++i) {
464		BPackageResolvable* resolvable = providesList.ItemAt(i);
465		bool hasVersion = resolvable->Version().InitCheck() == B_OK;
466		bool hasCompatibleVersion
467			= resolvable->CompatibleVersion().InitCheck() == B_OK;
468
469		PackageAttribute* provides = new PackageAttribute(
470			B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES, B_HPKG_ATTRIBUTE_TYPE_STRING,
471			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
472		provides->string = fPackageStringCache.Get(resolvable->Name().String());
473		attributeList.Add(provides);
474
475		PackageAttribute* providesType = new PackageAttribute(
476			B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES_TYPE,
477			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
478		providesType->unsignedInt = resolvable->Type();
479		provides->children.Add(providesType);
480
481		if (hasVersion)
482			RegisterPackageVersion(provides->children, resolvable->Version());
483
484		if (hasCompatibleVersion) {
485			RegisterPackageVersion(provides->children,
486				resolvable->CompatibleVersion(),
487				B_HPKG_ATTRIBUTE_ID_PACKAGE_PROVIDES_COMPATIBLE);
488		}
489	}
490
491	// requires list
492	RegisterPackageResolvableExpressionList(attributeList,
493		packageInfo.RequiresList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_REQUIRES);
494
495	// supplements list
496	RegisterPackageResolvableExpressionList(attributeList,
497		packageInfo.SupplementsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_SUPPLEMENTS);
498
499	// conflicts list
500	RegisterPackageResolvableExpressionList(attributeList,
501		packageInfo.ConflictsList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_CONFLICTS);
502
503	// freshens list
504	RegisterPackageResolvableExpressionList(attributeList,
505		packageInfo.FreshensList(), B_HPKG_ATTRIBUTE_ID_PACKAGE_FRESHENS);
506
507	// replaces list
508	const BStringList& replacesList = packageInfo.ReplacesList();
509	for (int i = 0; i < replacesList.CountStrings(); ++i) {
510		PackageAttribute* replaces = new PackageAttribute(
511			B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES, B_HPKG_ATTRIBUTE_TYPE_STRING,
512			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
513		replaces->string = fPackageStringCache.Get(replacesList.StringAt(i));
514		attributeList.Add(replaces);
515	}
516
517	// checksum (optional, only exists in repositories)
518	if (packageInfo.Checksum().Length() > 0) {
519		PackageAttribute* checksum = new PackageAttribute(
520			B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM, B_HPKG_ATTRIBUTE_TYPE_STRING,
521			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
522		checksum->string
523			= fPackageStringCache.Get(packageInfo.Checksum().String());
524		attributeList.Add(checksum);
525	}
526
527	// install path (optional)
528	if (!packageInfo.InstallPath().IsEmpty()) {
529		PackageAttribute* installPath = new PackageAttribute(
530			B_HPKG_ATTRIBUTE_ID_PACKAGE_INSTALL_PATH,
531			B_HPKG_ATTRIBUTE_TYPE_STRING,
532			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
533		installPath->string = fPackageStringCache.Get(
534			packageInfo.InstallPath().String());
535		attributeList.Add(installPath);
536	}
537}
538
539
540void
541WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
542	const BPackageVersion& version, BHPKGAttributeID attributeID)
543{
544	PackageAttribute* versionMajor = new PackageAttribute(
545		attributeID, B_HPKG_ATTRIBUTE_TYPE_STRING,
546		B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
547	versionMajor->string = fPackageStringCache.Get(version.Major().String());
548	attributeList.Add(versionMajor);
549
550	if (version.Minor().Length() > 0) {
551		PackageAttribute* versionMinor = new PackageAttribute(
552			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MINOR,
553			B_HPKG_ATTRIBUTE_TYPE_STRING,
554			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
555		versionMinor->string
556			= fPackageStringCache.Get(version.Minor().String());
557		versionMajor->children.Add(versionMinor);
558
559		if (version.Micro().Length() > 0) {
560			PackageAttribute* versionMicro = new PackageAttribute(
561				B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_MICRO,
562				B_HPKG_ATTRIBUTE_TYPE_STRING,
563				B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
564			versionMicro->string
565				= fPackageStringCache.Get(version.Micro().String());
566			versionMajor->children.Add(versionMicro);
567		}
568	}
569
570	if (!version.PreRelease().IsEmpty()) {
571		PackageAttribute* preRelease = new PackageAttribute(
572			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
573			B_HPKG_ATTRIBUTE_TYPE_STRING,
574			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
575		preRelease->string
576			= fPackageStringCache.Get(version.PreRelease().String());
577		versionMajor->children.Add(preRelease);
578	}
579
580	if (version.Release() != 0) {
581		PackageAttribute* versionRelease = new PackageAttribute(
582			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE,
583			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
584		versionRelease->unsignedInt = version.Release();
585		versionMajor->children.Add(versionRelease);
586	}
587}
588
589
590void
591WriterImplBase::RegisterPackageResolvableExpressionList(
592	PackageAttributeList& attributeList,
593	const BObjectList<BPackageResolvableExpression>& expressionList, uint8 id)
594{
595	for (int i = 0; i < expressionList.CountItems(); ++i) {
596		BPackageResolvableExpression* resolvableExpr = expressionList.ItemAt(i);
597		bool hasVersion = resolvableExpr->Version().InitCheck() == B_OK;
598
599		PackageAttribute* name = new PackageAttribute((BHPKGAttributeID)id,
600			B_HPKG_ATTRIBUTE_TYPE_STRING,
601			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
602		name->string = fPackageStringCache.Get(resolvableExpr->Name().String());
603		attributeList.Add(name);
604
605		if (hasVersion) {
606			PackageAttribute* op = new PackageAttribute(
607				B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR,
608				B_HPKG_ATTRIBUTE_TYPE_UINT,
609				B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT);
610			op->unsignedInt = resolvableExpr->Operator();
611			name->children.Add(op);
612			RegisterPackageVersion(name->children, resolvableExpr->Version());
613		}
614	}
615}
616
617
618int32
619WriterImplBase::WriteCachedStrings(const StringCache& cache,
620	uint32 minUsageCount)
621{
622	// create an array of the cached strings
623	int32 count = cache.CountElements();
624	CachedString** cachedStrings = new CachedString*[count];
625	ArrayDeleter<CachedString*> cachedStringsDeleter(cachedStrings);
626
627	int32 index = 0;
628	for (CachedStringTable::Iterator it = cache.GetIterator();
629			CachedString* string = it.Next();) {
630		cachedStrings[index++] = string;
631	}
632
633	// sort it by descending usage count
634	std::sort(cachedStrings, cachedStrings + count, CachedStringUsageGreater());
635
636	// assign the indices and write entries to disk
637	int32 stringsWritten = 0;
638	for (int32 i = 0; i < count; i++) {
639		CachedString* cachedString = cachedStrings[i];
640
641		// empty strings must be stored inline, as they can't be distinguished
642		// from the end-marker!
643		if (strlen(cachedString->string) == 0)
644			continue;
645
646		// strings that are used only once are better stored inline
647		if (cachedString->usageCount < minUsageCount)
648			break;
649
650		WriteString(cachedString->string);
651
652		cachedString->index = stringsWritten++;
653	}
654
655	// write a terminating 0 byte
656	Write<uint8>(0);
657
658	return stringsWritten;
659}
660
661
662int32
663WriterImplBase::WritePackageAttributes(
664	const PackageAttributeList& packageAttributes,
665	uint32& _stringsLengthUncompressed)
666{
667	// write the cached strings
668	uint32 stringsCount = WriteCachedStrings(fPackageStringCache, 2);
669	_stringsLengthUncompressed = DataWriter()->BytesWritten();
670
671	_WritePackageAttributes(packageAttributes);
672
673	return stringsCount;
674}
675
676
677void
678WriterImplBase::WriteAttributeValue(const AttributeValue& value, uint8 encoding)
679{
680	switch (value.type) {
681		case B_HPKG_ATTRIBUTE_TYPE_INT:
682		case B_HPKG_ATTRIBUTE_TYPE_UINT:
683		{
684			uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT
685				? (uint64)value.signedInt : value.unsignedInt;
686
687			switch (encoding) {
688				case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT:
689					Write<uint8>((uint8)intValue);
690					break;
691				case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT:
692					Write<uint16>(
693						B_HOST_TO_BENDIAN_INT16((uint16)intValue));
694					break;
695				case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT:
696					Write<uint32>(
697						B_HOST_TO_BENDIAN_INT32((uint32)intValue));
698					break;
699				case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT:
700					Write<uint64>(
701						B_HOST_TO_BENDIAN_INT64((uint64)intValue));
702					break;
703				default:
704				{
705					fErrorOutput->PrintError("WriteAttributeValue(): invalid "
706						"encoding %d for int value type %d\n", encoding,
707						value.type);
708					throw status_t(B_BAD_VALUE);
709				}
710			}
711
712			break;
713		}
714
715		case B_HPKG_ATTRIBUTE_TYPE_STRING:
716		{
717			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE)
718				WriteUnsignedLEB128(value.string->index);
719			else
720				WriteString(value.string->string);
721			break;
722		}
723
724		case B_HPKG_ATTRIBUTE_TYPE_RAW:
725		{
726			WriteUnsignedLEB128(value.data.size);
727			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP)
728				WriteUnsignedLEB128(value.data.offset);
729			else
730				fDataWriter->WriteDataThrows(value.data.raw, value.data.size);
731			break;
732		}
733
734		default:
735			fErrorOutput->PrintError(
736				"WriteAttributeValue(): invalid value type: %d\n", value.type);
737			throw status_t(B_BAD_VALUE);
738	}
739}
740
741
742void
743WriterImplBase::WriteUnsignedLEB128(uint64 value)
744{
745	uint8 bytes[10];
746	int32 count = 0;
747	do {
748		uint8 byte = value & 0x7f;
749		value >>= 7;
750		bytes[count++] = byte | (value != 0 ? 0x80 : 0);
751	} while (value != 0);
752
753	fDataWriter->WriteDataThrows(bytes, count);
754}
755
756
757void
758WriterImplBase::WriteBuffer(const void* buffer, size_t size, off_t offset)
759{
760	ssize_t bytesWritten = pwrite(fFD, buffer, size, offset);
761	if (bytesWritten < 0) {
762		fErrorOutput->PrintError(
763			"WriteBuffer(%p, %lu) failed to write data: %s\n", buffer, size,
764			strerror(errno));
765		throw status_t(errno);
766	}
767	if ((size_t)bytesWritten != size) {
768		fErrorOutput->PrintError(
769			"WriteBuffer(%p, %lu) failed to write all data\n", buffer, size);
770		throw status_t(B_ERROR);
771	}
772}
773
774
775void
776WriterImplBase::_WritePackageAttributes(
777	const PackageAttributeList& packageAttributes)
778{
779	DoublyLinkedList<PackageAttribute>::ConstIterator it
780		= packageAttributes.GetIterator();
781	while (PackageAttribute* attribute = it.Next()) {
782		uint8 encoding = attribute->ApplicableEncoding();
783
784		// write tag
785		WriteUnsignedLEB128(HPKG_ATTRIBUTE_TAG_COMPOSE(
786			attribute->id, attribute->type, encoding,
787			!attribute->children.IsEmpty()));
788
789		// write value
790		WriteAttributeValue(*attribute, encoding);
791
792		if (!attribute->children.IsEmpty())
793			_WritePackageAttributes(attribute->children);
794	}
795
796	WriteUnsignedLEB128(0);
797}
798
799
800}	// namespace BPrivate
801
802}	// namespace BHPKG
803
804}	// namespace BPackageKit
805