xref: /haiku/src/kits/tracker/AttributeStream.cpp (revision 32832cbe47f991cc6d2b29824903181d8baaaa63)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 
36 #include "AttributeStream.h"
37 
38 #include <Debug.h>
39 #include <Node.h>
40 
41 
42 // ToDo:
43 // lazy Rewind from Drive, only if data is available
44 // BMessage node
45 // partial feeding (part, not the whole buffer)
46 
47 
48 //	#pragma mark - AttributeInfo
49 
50 
51 AttributeInfo::AttributeInfo(const AttributeInfo &cloneThis)
52 	:
53 	fName(cloneThis.fName),
54 	fInfo(cloneThis.fInfo)
55 
56 {
57 }
58 
59 
60 AttributeInfo::AttributeInfo(const char* name, attr_info info)
61 	:
62 	fName(name),
63 	fInfo(info)
64 {
65 }
66 
67 
68 AttributeInfo::AttributeInfo(const char* name, uint32 type, off_t size)
69 	:
70 	fName(name)
71 {
72 	fInfo.size = size;
73 	fInfo.type = type;
74 }
75 
76 
77 const char*
78 AttributeInfo::Name() const
79 {
80 	return fName.String();
81 }
82 
83 
84 uint32
85 AttributeInfo::Type() const
86 {
87 	return fInfo.type;
88 }
89 
90 
91 off_t
92 AttributeInfo::Size() const
93 {
94 	return fInfo.size;
95 }
96 
97 
98 void
99 AttributeInfo::SetTo(const AttributeInfo &attr)
100 {
101 	fName = attr.fName;
102 	fInfo = attr.fInfo;
103 }
104 
105 
106 void
107 AttributeInfo::SetTo(const char* name, attr_info info)
108 {
109 	fName = name;
110 	fInfo = info;
111 }
112 
113 
114 void
115 AttributeInfo::SetTo(const char* name, uint32 type, off_t size)
116 {
117 	fName = name;
118 	fInfo.type = type;
119 	fInfo.size = size;
120 }
121 
122 
123 //	#pragma mark - AttributeStreamNode
124 
125 
126 AttributeStreamNode::AttributeStreamNode()
127 	:
128 	fReadFrom(NULL),
129 	fWriteTo(NULL)
130 {
131 }
132 
133 
134 AttributeStreamNode::~AttributeStreamNode()
135 {
136 	Detach();
137 }
138 
139 
140 AttributeStreamNode&
141 AttributeStreamNode::operator<<(AttributeStreamNode &source)
142 {
143 	fReadFrom = &source;
144 	fReadFrom->fWriteTo = this;
145 	if (fReadFrom->CanFeed())
146 		fReadFrom->Start();
147 
148 	return source;
149 }
150 
151 
152 void
153 AttributeStreamNode::Rewind()
154 {
155 	if (fReadFrom != NULL)
156 		fReadFrom->Rewind();
157 }
158 
159 
160 void
161 AttributeStreamFileNode::MakeEmpty()
162 {
163 	TRESPASS();
164 }
165 
166 
167 off_t
168 AttributeStreamNode::Contains(const char* name, uint32 type)
169 {
170 	if (fReadFrom == NULL)
171 		return 0;
172 
173 	return fReadFrom->Contains(name, type);
174 }
175 
176 
177 off_t
178 AttributeStreamNode::Read(const char* name, const char* foreignName,
179 	uint32 type, off_t size, void* buffer, void (*swapFunc)(void*))
180 {
181 	if (fReadFrom == NULL)
182 		return 0;
183 
184 	return fReadFrom->Read(name, foreignName, type, size, buffer, swapFunc);
185 }
186 
187 
188 off_t
189 AttributeStreamNode::Write(const char* name, const char* foreignName,
190 	uint32 type, off_t size, const void* buffer)
191 {
192 	if (fWriteTo == NULL)
193 		return 0;
194 
195 	return fWriteTo->Write(name, foreignName, type, size, buffer);
196 }
197 
198 
199 bool
200 AttributeStreamNode::Drive()
201 {
202 	ASSERT(CanFeed());
203 	if (fReadFrom == NULL)
204 		return false;
205 
206 	Rewind();
207 	return true;
208 }
209 
210 
211 const AttributeInfo*
212 AttributeStreamNode::Next()
213 {
214 	if (fReadFrom != NULL)
215 		return fReadFrom->Next();
216 
217 	return NULL;
218 }
219 
220 
221 const char*
222 AttributeStreamNode::Get()
223 {
224 	ASSERT(fReadFrom != NULL);
225 
226 	return fReadFrom->Get();
227 }
228 
229 
230 bool
231 AttributeStreamNode::Fill(char* buffer) const
232 {
233 	ASSERT(fReadFrom != NULL);
234 
235 	return fReadFrom->Fill(buffer);
236 }
237 
238 
239 bool
240 AttributeStreamNode::Start()
241 {
242 	if (fWriteTo == NULL) {
243 		// we are at the head of the stream, start drivin'
244 		return Drive();
245 	}
246 
247 	return fWriteTo->Start();
248 }
249 
250 
251 void
252 AttributeStreamNode::Detach()
253 {
254 	AttributeStreamNode* tmpFrom = fReadFrom;
255 	AttributeStreamNode* tmpTo = fWriteTo;
256 	fReadFrom = NULL;
257 	fWriteTo = NULL;
258 
259 	if (tmpFrom != NULL)
260 		tmpFrom->Detach();
261 
262 	if (tmpTo != NULL)
263 		tmpTo->Detach();
264 }
265 
266 
267 //	#pragma mark - AttributeStreamFileNode
268 
269 
270 AttributeStreamFileNode::AttributeStreamFileNode()
271 	:
272 	fNode(NULL)
273 {
274 }
275 
276 
277 AttributeStreamFileNode::AttributeStreamFileNode(BNode* node)
278 	:
279 	fNode(node)
280 {
281 	ASSERT(fNode);
282 }
283 
284 
285 void
286 AttributeStreamFileNode::Rewind()
287 {
288 	_inherited::Rewind();
289 	fNode->RewindAttrs();
290 }
291 
292 
293 void
294 AttributeStreamFileNode::SetTo(BNode* node)
295 {
296 	fNode = node;
297 }
298 
299 
300 off_t
301 AttributeStreamFileNode::Contains(const char* name, uint32 type)
302 {
303 	ASSERT(fNode);
304 	attr_info info;
305 	if (fNode->GetAttrInfo(name, &info) != B_OK)
306 		return 0;
307 
308 	if (info.type != type)
309 		return 0;
310 
311 	return info.size;
312 }
313 
314 
315 off_t
316 AttributeStreamFileNode::Read(const char* name, const char* foreignName,
317 	uint32 type, off_t size, void* buffer, void (*swapFunc)(void*))
318 {
319 	if (name != NULL
320 		&& fNode->ReadAttr(name, type, 0, buffer, (size_t)size) == size) {
321 		return size;
322 	}
323 
324 	// didn't find the attribute under the native name, try the foreign name
325 	if (foreignName != NULL && fNode->ReadAttr(foreignName, type, 0, buffer,
326 			(size_t)size) == size) {
327 		// foreign attribute, swap the data
328 		if (swapFunc != NULL)
329 			(swapFunc)(buffer);
330 
331 		return size;
332 	}
333 
334 	return 0;
335 }
336 
337 
338 off_t
339 AttributeStreamFileNode::Write(const char* name, const char* foreignName,
340 	uint32 type, off_t size, const void* buffer)
341 {
342 	ASSERT(fNode != NULL);
343 	ASSERT(dynamic_cast<BNode*>(fNode) != NULL);
344 
345 	off_t result = fNode->WriteAttr(name, type, 0, buffer, (size_t)size);
346 	if (result == size && foreignName != NULL) {
347 		// the write operation worked fine, remove the foreign attribute
348 		// to not let stale data hang around
349 		fNode->RemoveAttr(foreignName);
350 	}
351 
352 	return result;
353 }
354 
355 
356 bool
357 AttributeStreamFileNode::Drive()
358 {
359 	ASSERT(fNode != NULL);
360 	if (!_inherited::Drive())
361 		return false;
362 
363 	const AttributeInfo* attr;
364 	while ((attr = fReadFrom->Next()) != 0) {
365 		const char* data = fReadFrom->Get();
366 		off_t result = fNode->WriteAttr(attr->Name(), attr->Type(), 0,
367 			data, (size_t)attr->Size());
368 		if (result < attr->Size())
369 			return true;
370 	}
371 
372 	return true;
373 }
374 
375 
376 const char*
377 AttributeStreamFileNode::Get()
378 {
379 	ASSERT(fNode != NULL);
380 	TRESPASS();
381 
382 	return NULL;
383 }
384 
385 
386 bool
387 AttributeStreamFileNode::Fill(char* buffer) const
388 {
389 	ASSERT(fNode != NULL);
390 
391 	return fNode->ReadAttr(fCurrentAttr.Name(), fCurrentAttr.Type(), 0,
392 		buffer, (size_t)fCurrentAttr.Size()) == (ssize_t)fCurrentAttr.Size();
393 }
394 
395 
396 const AttributeInfo*
397 AttributeStreamFileNode::Next()
398 {
399 	ASSERT(fNode != NULL);
400 	ASSERT(fReadFrom == NULL);
401 
402 	char attrName[256];
403 	if (fNode->GetNextAttrName(attrName) != B_OK)
404 		return NULL;
405 
406 	attr_info info;
407 	if (fNode->GetAttrInfo(attrName, &info) != B_OK)
408 		return NULL;
409 
410 	fCurrentAttr.SetTo(attrName, info);
411 
412 	return &fCurrentAttr;
413 }
414 
415 
416 //	#pragma mark - AttributeStreamMemoryNode
417 
418 
419 AttributeStreamMemoryNode::AttributeStreamMemoryNode()
420 	:
421 	fAttributes(5, true),
422 	fCurrentIndex(-1)
423 {
424 }
425 
426 
427 void
428 AttributeStreamMemoryNode::MakeEmpty()
429 {
430 	fAttributes.MakeEmpty();
431 }
432 
433 
434 void
435 AttributeStreamMemoryNode::Rewind()
436 {
437 	_inherited::Rewind();
438 	fCurrentIndex = -1;
439 }
440 
441 
442 int32
443 AttributeStreamMemoryNode::Find(const char* name, uint32 type) const
444 {
445 	int32 count = fAttributes.CountItems();
446 	for (int32 index = 0; index < count; index++) {
447 		if (strcmp(fAttributes.ItemAt(index)->fAttr.Name(), name) == 0
448 			&& fAttributes.ItemAt(index)->fAttr.Type() == type) {
449 			return index;
450 		}
451 	}
452 
453 	return -1;
454 }
455 
456 
457 off_t
458 AttributeStreamMemoryNode::Contains(const char* name, uint32 type)
459 {
460 	int32 index = Find(name, type);
461 
462 	return index < 0 ? 0 : fAttributes.ItemAt(index)->fAttr.Size();
463 }
464 
465 
466 off_t
467 AttributeStreamMemoryNode::Read(const char* name,
468 	const char* DEBUG_ONLY(foreignName), uint32 type, off_t bufferSize,
469 	void* buffer, void (*DEBUG_ONLY(swapFunc))(void*))
470 {
471 	ASSERT(!foreignName);
472 	ASSERT(!swapFunc);
473 
474 	AttrNode* attrNode = NULL;
475 
476 	int32 index = Find(name, type);
477 	if (index < 0) {
478 		if (fReadFrom == NULL)
479 			return 0;
480 
481 		off_t size = fReadFrom->Contains(name, type);
482 		if (size == 0)
483 			return 0;
484 
485 		attrNode = BufferingGet(name, type, size);
486 		if (attrNode == NULL)
487 			return 0;
488 	} else
489 		attrNode = fAttributes.ItemAt(index);
490 
491 	if (attrNode->fAttr.Size() > bufferSize)
492 		return 0;
493 
494 	memcpy(buffer, attrNode->fData, (size_t)attrNode->fAttr.Size());
495 
496 	return attrNode->fAttr.Size();
497 }
498 
499 
500 off_t
501 AttributeStreamMemoryNode::Write(const char* name, const char*, uint32 type,
502 	off_t size, const void* buffer)
503 {
504 	char* newBuffer = new char[size];
505 	memcpy(newBuffer, buffer, (size_t)size);
506 
507 	AttrNode* attrNode = new AttrNode(name, type, size, newBuffer);
508 	fAttributes.AddItem(attrNode);
509 
510 	return size;
511 }
512 
513 
514 bool
515 AttributeStreamMemoryNode::Drive()
516 {
517 	if (!_inherited::Drive())
518 		return false;
519 
520 	while (BufferingGet())
521 		;
522 
523 	return true;
524 }
525 
526 
527 AttributeStreamMemoryNode::AttrNode*
528 AttributeStreamMemoryNode::BufferingGet(const char* name, uint32 type,
529 	off_t size)
530 {
531 	char* newBuffer = new char[size];
532 	if (!fReadFrom->Fill(newBuffer)) {
533 		delete[] newBuffer;
534 		return NULL;
535 	}
536 
537 	AttrNode* attrNode = new AttrNode(name, type, size, newBuffer);
538 	fAttributes.AddItem(attrNode);
539 
540 	return fAttributes.LastItem();
541 }
542 
543 
544 AttributeStreamMemoryNode::AttrNode*
545 AttributeStreamMemoryNode::BufferingGet()
546 {
547 	if (fReadFrom == NULL)
548 		return NULL;
549 
550 	const AttributeInfo* attr = fReadFrom->Next();
551 	if (attr == NULL)
552 		return NULL;
553 
554 	return BufferingGet(attr->Name(), attr->Type(), attr->Size());
555 }
556 
557 
558 const AttributeInfo*
559 AttributeStreamMemoryNode::Next()
560 {
561 	if (fReadFrom != NULL) {
562 		// the buffer is in the middle of the stream, get
563 		// one buffer at a time
564 		BufferingGet();
565 	}
566 
567 	if (fCurrentIndex + 1 >= fAttributes.CountItems())
568 		return NULL;
569 
570 	return &fAttributes.ItemAt(++fCurrentIndex)->fAttr;
571 }
572 
573 
574 const char*
575 AttributeStreamMemoryNode::Get()
576 {
577 	ASSERT(fCurrentIndex < fAttributes.CountItems());
578 
579 	return fAttributes.ItemAt(fCurrentIndex)->fData;
580 }
581 
582 
583 bool
584 AttributeStreamMemoryNode::Fill(char* buffer) const
585 {
586 	ASSERT(fCurrentIndex < fAttributes.CountItems());
587 	memcpy(buffer, fAttributes.ItemAt(fCurrentIndex)->fData,
588 		(size_t)fAttributes.ItemAt(fCurrentIndex)->fAttr.Size());
589 
590 	return true;
591 }
592 
593 
594 //	#pragma mark - AttributeStreamTemplateNode
595 
596 
597 AttributeStreamTemplateNode::AttributeStreamTemplateNode(
598 	const AttributeTemplate* attrTemplates, int32 count)
599 	:
600 	fAttributes(attrTemplates),
601 	fCurrentIndex(-1),
602 	fCount(count)
603 {
604 }
605 
606 
607 off_t
608 AttributeStreamTemplateNode::Contains(const char* name, uint32 type)
609 {
610 	int32 index = Find(name, type);
611 	if (index < 0)
612 		return 0;
613 
614 	return fAttributes[index].fSize;
615 }
616 
617 
618 void
619 AttributeStreamTemplateNode::Rewind()
620 {
621 	fCurrentIndex = -1;
622 }
623 
624 
625 const AttributeInfo*
626 AttributeStreamTemplateNode::Next()
627 {
628 	if (fCurrentIndex + 1 >= fCount)
629 		return NULL;
630 
631 	++fCurrentIndex;
632 
633 	fCurrentAttr.SetTo(fAttributes[fCurrentIndex].fAttributeName,
634 		fAttributes[fCurrentIndex].fAttributeType,
635 		fAttributes[fCurrentIndex].fSize);
636 
637 	return &fCurrentAttr;
638 }
639 
640 
641 const char*
642 AttributeStreamTemplateNode::Get()
643 {
644 	ASSERT(fCurrentIndex < fCount);
645 
646 	return fAttributes[fCurrentIndex].fBits;
647 }
648 
649 
650 bool
651 AttributeStreamTemplateNode::Fill(char* buffer) const
652 {
653 	ASSERT(fCurrentIndex < fCount);
654 	memcpy(buffer, fAttributes[fCurrentIndex].fBits,
655 		(size_t)fAttributes[fCurrentIndex].fSize);
656 
657 	return true;
658 }
659 
660 
661 int32
662 AttributeStreamTemplateNode::Find(const char* name, uint32 type) const
663 {
664 	for (int32 index = 0; index < fCount; index++) {
665 		if (fAttributes[index].fAttributeType == type &&
666 			strcmp(name, fAttributes[index].fAttributeName) == 0) {
667 			return index;
668 		}
669 	}
670 
671 	return -1;
672 }
673 
674 
675 //	#pragma mark - AttributeStreamFilterNode
676 
677 
678 bool
679 AttributeStreamFilterNode::Reject(const char*, uint32, off_t)
680 {
681 	// simple pass everything filter
682 	return false;
683 }
684 
685 
686 const AttributeInfo*
687 AttributeStreamFilterNode::Next()
688 {
689 	if (fReadFrom == NULL)
690 		return NULL;
691 
692 	for (;;) {
693 		const AttributeInfo* attr = fReadFrom->Next();
694 		if (attr == NULL)
695 			break;
696 
697 		if (!Reject(attr->Name(), attr->Type(), attr->Size()))
698 			return attr;
699 	}
700 
701 	return NULL;
702 }
703 
704 
705 off_t
706 AttributeStreamFilterNode::Contains(const char* name, uint32 type)
707 {
708 	if (fReadFrom == NULL)
709 		return 0;
710 
711 	off_t size = fReadFrom->Contains(name, type);
712 
713 	if (!Reject(name, type, size))
714 		return size;
715 
716 	return 0;
717 }
718 
719 
720 off_t
721 AttributeStreamFilterNode::Read(const char* name, const char* foreignName,
722 	uint32 type, off_t size, void* buffer, void (*swapFunc)(void*))
723 {
724 	if (fReadFrom == NULL)
725 		return 0;
726 
727 	if (!Reject(name, type, size)) {
728 		return fReadFrom->Read(name, foreignName, type, size, buffer,
729 			swapFunc);
730 	}
731 
732 	return 0;
733 }
734 
735 
736 off_t
737 AttributeStreamFilterNode::Write(const char* name, const char* foreignName,
738 	uint32 type, off_t size, const void* buffer)
739 {
740 	if (fWriteTo == NULL)
741 		return 0;
742 
743 	if (!Reject(name, type, size))
744 		return fWriteTo->Write(name, foreignName, type, size, buffer);
745 
746 	return size;
747 }
748 
749 
750 //	#pragma mark - NamesToAcceptAttrFilter
751 
752 
753 NamesToAcceptAttrFilter::NamesToAcceptAttrFilter(const char** nameList)
754 	:
755 	fNameList(nameList)
756 {
757 }
758 
759 
760 bool
761 NamesToAcceptAttrFilter::Reject(const char* name, uint32, off_t)
762 {
763 	for (int32 index = 0; ; index++) {
764 		if (fNameList[index] == NULL)
765 			break;
766 
767 		if (strcmp(name, fNameList[index]) == 0) {
768 			//PRINT(("filter passing through %s\n", name));
769 			return false;
770 		}
771 	}
772 
773 	//PRINT(("filter rejecting %s\n", name));
774 	return true;
775 }
776 
777 
778 //	#pragma mark - SelectiveAttributeTransformer
779 
780 
781 SelectiveAttributeTransformer::SelectiveAttributeTransformer(
782 	const char* attributeName,
783 	bool (*transformFunc)(const char* , uint32 , off_t, void*, void*),
784 	void* params)
785 	:
786 	fAttributeNameToTransform(attributeName),
787 	fTransformFunc(transformFunc),
788 	fTransformParams(params),
789 	fTransformedBuffers(10, false)
790 {
791 }
792 
793 
794 SelectiveAttributeTransformer::~SelectiveAttributeTransformer()
795 {
796 	for (int32 index = fTransformedBuffers.CountItems() - 1; index >= 0;
797 			index--) {
798 		delete[] fTransformedBuffers.ItemAt(index);
799 	}
800 }
801 
802 
803 void
804 SelectiveAttributeTransformer::Rewind()
805 {
806 	for (int32 index = fTransformedBuffers.CountItems() - 1; index >= 0;
807 			index--) {
808 		delete[] fTransformedBuffers.ItemAt(index);
809 	}
810 
811 	fTransformedBuffers.MakeEmpty();
812 }
813 
814 
815 off_t
816 SelectiveAttributeTransformer::Read(const char* name, const char* foreignName,
817 	uint32 type, off_t size, void* buffer, void (*swapFunc)(void*))
818 {
819 	if (fReadFrom == NULL)
820 		return 0;
821 
822 	off_t result = fReadFrom->Read(name, foreignName, type, size, buffer,
823 		swapFunc);
824 
825 	if (WillTransform(name, type, size, (const char*)buffer))
826 		ApplyTransformer(name, type, size, (char*)buffer);
827 
828 	return result;
829 }
830 
831 
832 bool
833 SelectiveAttributeTransformer::WillTransform(const char* name, uint32, off_t,
834 	const char*) const
835 {
836 	return strcmp(name, fAttributeNameToTransform) == 0;
837 }
838 
839 
840 bool
841 SelectiveAttributeTransformer::ApplyTransformer(const char* name, uint32 type,
842 	off_t size, char* data)
843 {
844 	return (fTransformFunc)(name, type, size, data, fTransformParams);
845 }
846 
847 char*
848 SelectiveAttributeTransformer::CopyAndApplyTransformer(const char* name,
849 	uint32 type, off_t size, const char* data)
850 {
851 	char* result = NULL;
852 
853 	if (data != NULL) {
854 		result = new char[size];
855 		memcpy(result, data, (size_t)size);
856 	}
857 
858 	if (!(fTransformFunc)(name, type, size, result, fTransformParams)) {
859 		delete[] result;
860 		return NULL;
861 	}
862 
863 	return result;
864 }
865 
866 
867 const AttributeInfo*
868 SelectiveAttributeTransformer::Next()
869 {
870 	const AttributeInfo* result = fReadFrom->Next();
871 
872 	if (result == NULL)
873 		return NULL;
874 
875 	fCurrentAttr.SetTo(*result);
876 	return result;
877 }
878 
879 
880 const char*
881 SelectiveAttributeTransformer::Get()
882 {
883 	if (fReadFrom == NULL)
884 		return NULL;
885 
886 	const char* result = fReadFrom->Get();
887 
888 	if (!WillTransform(fCurrentAttr.Name(), fCurrentAttr.Type(),
889 			fCurrentAttr.Size(), result)) {
890 		return result;
891 	}
892 
893 	char* transformedData = CopyAndApplyTransformer(fCurrentAttr.Name(),
894 		fCurrentAttr.Type(), fCurrentAttr.Size(), result);
895 
896 	// enlist for proper disposal when our job is done
897 	if (transformedData != NULL) {
898 		fTransformedBuffers.AddItem(transformedData);
899 		return transformedData;
900 	}
901 
902 	return result;
903 }
904