2
* Copyright (C) 2006 The Android Open Source Project
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
#include <utils/BufferedTextOutput.h>
19
#include <utils/Atomic.h>
20
#include <utils/Debug.h>
21
#include <utils/Log.h>
22
#include <utils/RefBase.h>
23
#include <utils/Vector.h>
24
#include <cutils/threads.h>
26
#include <private/utils/Static.h>
31
// ---------------------------------------------------------------------------
35
struct BufferedTextOutput::BufferState : public RefBase
37
BufferState(int32_t _seq)
50
status_t append(const char* txt, size_t len) {
51
if ((len+bufferPos) > bufferSize) {
52
void* b = realloc(buffer, ((len+bufferPos)*3)/2);
53
if (!b) return NO_MEMORY;
56
memcpy(buffer+bufferPos, txt, len);
64
if (bufferSize > 256) {
65
void* b = realloc(buffer, 256);
82
struct BufferedTextOutput::ThreadState
84
Vector<sp<BufferedTextOutput::BufferState> > states;
87
static mutex_t gMutex;
89
static thread_store_t tls;
91
BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
93
ThreadState* ts = (ThreadState*) thread_store_get( &tls );
96
thread_store_set( &tls, ts, threadDestructor );
100
void BufferedTextOutput::threadDestructor(void *st)
102
delete ((ThreadState*)st);
105
static volatile int32_t gSequence = 0;
107
static volatile int32_t gFreeBufferIndex = -1;
109
static int32_t allocBufferIndex()
115
if (gFreeBufferIndex >= 0) {
116
res = gFreeBufferIndex;
117
gFreeBufferIndex = gTextBuffers[res];
118
gTextBuffers.editItemAt(res) = -1;
121
res = gTextBuffers.size();
122
gTextBuffers.add(-1);
125
mutex_unlock(&gMutex);
130
static void freeBufferIndex(int32_t idx)
133
gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
134
gFreeBufferIndex = idx;
135
mutex_unlock(&gMutex);
138
// ---------------------------------------------------------------------------
140
BufferedTextOutput::BufferedTextOutput(uint32_t flags)
142
, mSeq(android_atomic_inc(&gSequence))
143
, mIndex(allocBufferIndex())
145
mGlobalState = new BufferState(mSeq);
146
if (mGlobalState) mGlobalState->incStrong(this);
149
BufferedTextOutput::~BufferedTextOutput()
151
if (mGlobalState) mGlobalState->decStrong(this);
152
freeBufferIndex(mIndex);
155
status_t BufferedTextOutput::print(const char* txt, size_t len)
157
//printf("BufferedTextOutput: printing %d\n", len);
160
BufferState* b = getBuffer();
162
const char* const end = txt+len;
167
// Find the next line.
168
const char* first = txt;
169
while (txt < end && *txt != '\n') txt++;
171
// Include this and all following empty lines.
172
while (txt < end && *txt == '\n') txt++;
174
// Special cases for first data on a line.
177
// If this is the start of a line, add the indent.
178
const char* prefix = stringForIndent(b->indent);
179
err = b->append(prefix, strlen(prefix));
180
if (err != NO_ERROR) return err;
182
} else if (*(txt-1) == '\n' && !b->bundle) {
183
// Fast path: if we are not indenting or bundling, and
184
// have been given one or more complete lines, just write
185
// them out without going through the buffer.
187
// Slurp up all of the lines.
188
const char* lastLine = txt+1;
190
if (*txt++ == '\n') lastLine = txt;
193
vec.iov_base = (void*)first;
194
vec.iov_len = lastLine-first;
195
//printf("Writing %d bytes of data!\n", vec.iov_len);
202
// Append the new text to the buffer.
203
err = b->append(first, txt-first);
204
if (err != NO_ERROR) return err;
205
b->atFront = *(txt-1) == '\n';
207
// If we have finished a line and are not bundling, write
209
//printf("Buffer is now %d bytes\n", b->bufferPos);
210
if (b->atFront && !b->bundle) {
212
vec.iov_base = b->buffer;
213
vec.iov_len = b->bufferPos;
214
//printf("Writing %d bytes of data!\n", vec.iov_len);
223
void BufferedTextOutput::moveIndent(int delta)
226
BufferState* b = getBuffer();
228
if (b->indent < 0) b->indent = 0;
231
void BufferedTextOutput::pushBundle()
234
BufferState* b = getBuffer();
238
void BufferedTextOutput::popBundle()
241
BufferState* b = getBuffer();
243
LOG_FATAL_IF(b->bundle < 0,
244
"TextOutput::popBundle() called more times than pushBundle()");
245
if (b->bundle < 0) b->bundle = 0;
247
if (b->bundle == 0) {
248
// Last bundle, write out data if it is complete. If it is not
249
// complete, don't write until the last line is done... this may
250
// or may not be the write thing to do, but it's the easiest.
251
if (b->bufferPos > 0 && b->atFront) {
253
vec.iov_base = b->buffer;
254
vec.iov_len = b->bufferPos;
261
BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
263
if ((mFlags&MULTITHREADED) != 0) {
264
ThreadState* ts = getThreadState();
266
while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL);
267
BufferState* bs = ts->states[mIndex].get();
268
if (bs != NULL && bs->seq == mSeq) return bs;
270
ts->states.editItemAt(mIndex) = new BufferState(mIndex);
271
bs = ts->states[mIndex].get();
272
if (bs != NULL) return bs;
279
}; // namespace android