1// RequestAllocator.cpp
2
3#include <new>
4
5#include "AreaSupport.h"
6#include "Compatibility.h"
7#include "Debug.h"
8#include "Port.h"
9#include "RequestAllocator.h"
10
11// constructor
12RequestAllocator::RequestAllocator(Port* port)
13	: fError(B_NO_INIT),
14	  fPort(NULL),
15	  fRequest(NULL),
16	  fRequestSize(0),
17	  fAllocatedAreaCount(0),
18	  fDeferredInitInfoCount(0),
19	  fRequestInPortBuffer(false)
20{
21	Init(port);
22}
23
24// destructor
25RequestAllocator::~RequestAllocator()
26{
27	Uninit();
28}
29
30// Init
31status_t
32RequestAllocator::Init(Port* port)
33{
34	Uninit();
35	if (port) {
36		fPort = port;
37		fError = fPort->InitCheck();
38	}
39	return fError;
40}
41
42// Uninit
43void
44RequestAllocator::Uninit()
45{
46	if (!fRequestInPortBuffer)
47		delete[] (uint8*)fRequest;
48	for (int32 i = 0; i < fAllocatedAreaCount; i++)
49		delete_area(fAllocatedAreas[i]);
50	fAllocatedAreaCount = 0;
51	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
52		if (fDeferredInitInfos[i].inPortBuffer)
53			delete[] fDeferredInitInfos[i].data;
54	}
55	fDeferredInitInfoCount = 0;
56	fError = B_NO_INIT;
57	fPort = NULL;
58	fRequest = NULL;
59	fRequestSize = 0;
60}
61
62// Error
63status_t
64RequestAllocator::Error() const
65{
66	return fError;
67}
68
69// FinishDeferredInit
70void
71RequestAllocator::FinishDeferredInit()
72{
73	if (fError != B_OK)
74		return;
75	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
76		DeferredInitInfo& info = fDeferredInitInfos[i];
77		if (info.inPortBuffer) {
78			if (info.size > 0)
79				memcpy((uint8*)fRequest + info.offset, info.data, info.size);
80			delete[] info.data;
81		}
82		PRINT(("RequestAllocator::FinishDeferredInit(): area: %ld, "
83			"offset: %ld, size: %ld\n", info.area, info.offset, info.size));
84		info.target->SetTo(info.area, info.offset, info.size);
85	}
86	fDeferredInitInfoCount = 0;
87}
88
89// AllocateRequest
90status_t
91RequestAllocator::AllocateRequest(int32 size)
92{
93	if (fError != B_OK)
94		RETURN_ERROR(fError);
95	if (size < (int32)sizeof(Request) || size > fPort->GetCapacity())
96		RETURN_ERROR(fError = B_BAD_VALUE);
97	fRequest = (Request*)fPort->GetBuffer();
98	fRequestSize = size;
99	fRequestInPortBuffer = true;
100	return B_OK;
101}
102
103// ReadRequest
104status_t
105RequestAllocator::ReadRequest()
106{
107	if (fError != B_OK)
108		RETURN_ERROR(fError);
109	if (fPort->GetMessageSize() < (int32)sizeof(Request))
110		RETURN_ERROR(fError = B_BAD_DATA);
111	// clone the request
112	fRequest = (Request*)new(nothrow) uint8[fPort->GetMessageSize()];
113	if (!fRequest)
114		RETURN_ERROR(fError = B_NO_MEMORY);
115	memcpy(fRequest, fPort->GetMessage(), fPort->GetMessageSize());
116	fRequestSize = fPort->GetMessageSize();
117	fRequestInPortBuffer = false;
118	// relocate the request
119	fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
120		&fAllocatedAreaCount);
121	RETURN_ERROR(fError);
122}
123
124// GetRequest
125Request*
126RequestAllocator::GetRequest() const
127{
128	return fRequest;
129}
130
131// GetRequestSize
132int32
133RequestAllocator::GetRequestSize() const
134{
135	return fRequestSize;
136}
137
138// AllocateAddress
139status_t
140RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
141	void** data, bool deferredInit)
142{
143	if (fError != B_OK)
144		return fError;
145	if (!fRequest)
146		RETURN_ERROR(B_NO_INIT);
147	if (size < 0)
148		RETURN_ERROR(B_BAD_VALUE);
149	if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
150		RETURN_ERROR(B_BAD_VALUE);
151	// fix the alignment -- valid is 1, 2, 4, 8
152	if (align <= 0 || size == 0 || (align & 0x1))
153		align = 1;
154	else if (align & 0x2)
155		align = 2;
156	else if (align & 0x4)
157		align = 4;
158	else
159		align = 8;
160	// check address location
161	// Currently we only support relocation of addresses inside the
162	// port buffer.
163	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
164	if (addressOffset < (int32)sizeof(Request)
165		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
166		RETURN_ERROR(B_BAD_VALUE);
167	}
168	// get the next free aligned offset in the port buffer
169	int32 offset = (fRequestSize + align - 1) / align * align;
170	// allocate the data
171	if (offset + size <= fPort->GetCapacity()) {
172		// there's enough free space in the port buffer
173		fRequestSize = offset + size;
174		if (deferredInit) {
175			DeferredInitInfo& info
176				= fDeferredInitInfos[fDeferredInitInfoCount];
177			if (size > 0) {
178				info.data = new(nothrow) uint8[size];
179				if (!info.data)
180					RETURN_ERROR(B_NO_MEMORY);
181			} else
182				info.data = NULL;
183			info.area = -1;
184			info.offset = offset;
185			info.size = size;
186			info.inPortBuffer = true;
187			info.target = &address;
188			*data = info.data;
189			fDeferredInitInfoCount++;
190		} else {
191			*data = (uint8*)fRequest + offset;
192			address.SetTo(-1, offset, size);
193		}
194	} else {
195		// not enough room in the port's buffer: we need to allocate an area
196		if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
197			RETURN_ERROR(B_ERROR);
198		int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
199		area_id area = create_area("request data", data,
200#ifdef _KERNEL_MODE
201			B_ANY_KERNEL_ADDRESS,
202#else
203			B_ANY_ADDRESS,
204#endif
205			areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
206		if (area < 0)
207			RETURN_ERROR(area);
208		fAllocatedAreas[fAllocatedAreaCount++] = area;
209		if (deferredInit) {
210			DeferredInitInfo& info
211				= fDeferredInitInfos[fDeferredInitInfoCount];
212			info.data = NULL;
213			info.area = area;
214			info.offset = 0;
215			info.size = size;
216			info.inPortBuffer = false;
217			info.target = &address;
218			fDeferredInitInfoCount++;
219PRINT(("  RequestAllocator::AllocateAddress(): deferred allocated area: "
220"%ld, size: %ld (%ld), data: %p\n", area, size, areaSize, *data));
221		} else
222			address.SetTo(area, 0, size);
223	}
224	return B_OK;
225}
226
227// AllocateData
228status_t
229RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
230	int32 align, bool deferredInit)
231{
232	void* destination;
233	status_t error = AllocateAddress(address, size, align, &destination,
234		deferredInit);
235	if (error != B_OK)
236		return error;
237	if (size > 0)
238		memcpy(destination, data, size);
239	return error;
240}
241
242// AllocateString
243status_t
244RequestAllocator::AllocateString(Address& address, const char* data,
245	bool deferredInit)
246{
247	int32 size = (data ? strlen(data) + 1 : 0);
248	return AllocateData(address, data, size, 1, deferredInit);
249}
250
251// SetAddress
252/*status_t
253RequestAllocator::SetAddress(Address& address, void* data, int32 size)
254{
255	if (fError != B_OK)
256		return fError;
257	if (!fRequest)
258		return (fError = B_NO_INIT);
259	// check address location
260	// Currently we only support relocation of addresses inside the
261	// port buffer.
262	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
263	if (addressOffset < (int32)sizeof(Request)
264		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
265		return (fError = B_BAD_VALUE);
266	}
267	// if data does itself lie within the port buffer, we store only the
268	// request relative offset
269	int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
270	if (!data) {
271		address.SetTo(-1, 0, 0);
272	} else if (inRequestOffset >= (int32)sizeof(Request)
273		&& inRequestOffset <= fRequestSize) {
274		if (inRequestOffset + size > fRequestSize)
275			return (fError = B_BAD_VALUE);
276		address.SetTo(-1, inRequestOffset, size);
277	} else {
278		// get the area and in-area offset for the address
279		area_id area;
280		int32 offset;
281		fError = get_area_for_address(data, size, &area, &offset);
282		if (fError != B_OK)
283			return fError;
284		// set the address
285		address.SetTo(area, offset, size);
286	}
287	return fError;
288}*/
289
290