xref: /haiku/src/kits/package/hpkg/WriterImplBase.cpp (revision 0d452c8f34013b611a54c746a71c05e28796eae2)
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 	fFlags(0),
318 	fFD(-1),
319 	fFinished(false),
320 	fDataWriter(NULL)
321 {
322 }
323 
324 
325 WriterImplBase::~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 
337 status_t
338 WriterImplBase::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 
362 void
363 WriterImplBase::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 
540 void
541 WriterImplBase::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.Revision() != 0) {
581 		PackageAttribute* versionRevision = new PackageAttribute(
582 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_REVISION,
583 			B_HPKG_ATTRIBUTE_TYPE_UINT, B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT);
584 		versionRevision->unsignedInt = version.Revision();
585 		versionMajor->children.Add(versionRevision);
586 	}
587 }
588 
589 
590 void
591 WriterImplBase::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 
618 int32
619 WriterImplBase::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 
662 int32
663 WriterImplBase::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 
677 void
678 WriterImplBase::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 
742 void
743 WriterImplBase::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 
757 void
758 WriterImplBase::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 
775 void
776 WriterImplBase::_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