blob: d516eb1d54245ba527777c4382ebdb56c44f5e23 [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
Mathias Agopian002e1e52013-05-06 20:20:50 -070017#include <binder/BufferedTextOutput.h>
18#include <binder/Debug.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080019
Steven Moreland2716e112018-02-23 14:57:20 -080020#include <cutils/atomic.h>
21#include <cutils/threads.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080022#include <utils/Log.h>
23#include <utils/RefBase.h>
24#include <utils/Vector.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080025
Mathias Agopian002e1e52013-05-06 20:20:50 -070026#include <private/binder/Static.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080027
Elliott Hughes5bf516f2018-07-13 11:13:42 -070028#include <pthread.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080029#include <stdio.h>
30#include <stdlib.h>
31
32// ---------------------------------------------------------------------------
33
34namespace android {
35
36struct BufferedTextOutput::BufferState : public RefBase
37{
Chih-Hung Hsiehe2347b72016-04-25 15:41:05 -070038 explicit BufferState(int32_t _seq)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080039 : seq(_seq)
Yi Kongfdd8da92018-06-07 17:52:27 -070040 , buffer(nullptr)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080041 , bufferPos(0)
42 , bufferSize(0)
43 , atFront(true)
44 , indent(0)
45 , bundle(0) {
46 }
47 ~BufferState() {
48 free(buffer);
49 }
50
51 status_t append(const char* txt, size_t len) {
52 if ((len+bufferPos) > bufferSize) {
Christopher Tateed7a50c2015-06-08 14:45:14 -070053 size_t newSize = ((len+bufferPos)*3)/2;
54 if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
55 void* b = realloc(buffer, newSize);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080056 if (!b) return NO_MEMORY;
57 buffer = (char*)b;
Christopher Tateed7a50c2015-06-08 14:45:14 -070058 bufferSize = newSize;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080059 }
60 memcpy(buffer+bufferPos, txt, len);
61 bufferPos += len;
62 return NO_ERROR;
63 }
64
65 void restart() {
66 bufferPos = 0;
67 atFront = true;
68 if (bufferSize > 256) {
69 void* b = realloc(buffer, 256);
70 if (b) {
71 buffer = (char*)b;
72 bufferSize = 256;
73 }
74 }
75 }
76
77 const int32_t seq;
78 char* buffer;
79 size_t bufferPos;
80 size_t bufferSize;
81 bool atFront;
82 int32_t indent;
83 int32_t bundle;
84};
85
86struct BufferedTextOutput::ThreadState
87{
88 Vector<sp<BufferedTextOutput::BufferState> > states;
89};
90
Elliott Hughes5bf516f2018-07-13 11:13:42 -070091static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080092
93static thread_store_t tls;
94
95BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
96{
97 ThreadState* ts = (ThreadState*) thread_store_get( &tls );
98 if (ts) return ts;
99 ts = new ThreadState;
100 thread_store_set( &tls, ts, threadDestructor );
101 return ts;
102}
103
104void BufferedTextOutput::threadDestructor(void *st)
105{
106 delete ((ThreadState*)st);
107}
108
109static volatile int32_t gSequence = 0;
110
111static volatile int32_t gFreeBufferIndex = -1;
112
113static int32_t allocBufferIndex()
114{
115 int32_t res = -1;
116
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700117 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800118
119 if (gFreeBufferIndex >= 0) {
120 res = gFreeBufferIndex;
121 gFreeBufferIndex = gTextBuffers[res];
122 gTextBuffers.editItemAt(res) = -1;
123
124 } else {
125 res = gTextBuffers.size();
126 gTextBuffers.add(-1);
127 }
128
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700129 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800130
131 return res;
132}
133
134static void freeBufferIndex(int32_t idx)
135{
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700136 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800137 gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
138 gFreeBufferIndex = idx;
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700139 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800140}
141
142// ---------------------------------------------------------------------------
143
144BufferedTextOutput::BufferedTextOutput(uint32_t flags)
145 : mFlags(flags)
146 , mSeq(android_atomic_inc(&gSequence))
147 , mIndex(allocBufferIndex())
148{
149 mGlobalState = new BufferState(mSeq);
150 if (mGlobalState) mGlobalState->incStrong(this);
151}
152
153BufferedTextOutput::~BufferedTextOutput()
154{
155 if (mGlobalState) mGlobalState->decStrong(this);
156 freeBufferIndex(mIndex);
157}
158
159status_t BufferedTextOutput::print(const char* txt, size_t len)
160{
161 //printf("BufferedTextOutput: printing %d\n", len);
162
163 AutoMutex _l(mLock);
164 BufferState* b = getBuffer();
165
166 const char* const end = txt+len;
167
168 status_t err;
169
170 while (txt < end) {
171 // Find the next line.
172 const char* first = txt;
173 while (txt < end && *txt != '\n') txt++;
174
175 // Include this and all following empty lines.
176 while (txt < end && *txt == '\n') txt++;
177
178 // Special cases for first data on a line.
179 if (b->atFront) {
180 if (b->indent > 0) {
181 // If this is the start of a line, add the indent.
182 const char* prefix = stringForIndent(b->indent);
183 err = b->append(prefix, strlen(prefix));
184 if (err != NO_ERROR) return err;
185
186 } else if (*(txt-1) == '\n' && !b->bundle) {
187 // Fast path: if we are not indenting or bundling, and
188 // have been given one or more complete lines, just write
189 // them out without going through the buffer.
190
191 // Slurp up all of the lines.
192 const char* lastLine = txt+1;
193 while (txt < end) {
194 if (*txt++ == '\n') lastLine = txt;
195 }
196 struct iovec vec;
197 vec.iov_base = (void*)first;
198 vec.iov_len = lastLine-first;
199 //printf("Writing %d bytes of data!\n", vec.iov_len);
200 writeLines(vec, 1);
201 txt = lastLine;
202 continue;
203 }
204 }
205
206 // Append the new text to the buffer.
207 err = b->append(first, txt-first);
208 if (err != NO_ERROR) return err;
209 b->atFront = *(txt-1) == '\n';
210
211 // If we have finished a line and are not bundling, write
212 // it out.
213 //printf("Buffer is now %d bytes\n", b->bufferPos);
214 if (b->atFront && !b->bundle) {
215 struct iovec vec;
216 vec.iov_base = b->buffer;
217 vec.iov_len = b->bufferPos;
218 //printf("Writing %d bytes of data!\n", vec.iov_len);
219 writeLines(vec, 1);
220 b->restart();
221 }
222 }
223
224 return NO_ERROR;
225}
226
227void BufferedTextOutput::moveIndent(int delta)
228{
229 AutoMutex _l(mLock);
230 BufferState* b = getBuffer();
231 b->indent += delta;
232 if (b->indent < 0) b->indent = 0;
233}
234
235void BufferedTextOutput::pushBundle()
236{
237 AutoMutex _l(mLock);
238 BufferState* b = getBuffer();
239 b->bundle++;
240}
241
242void BufferedTextOutput::popBundle()
243{
244 AutoMutex _l(mLock);
245 BufferState* b = getBuffer();
246 b->bundle--;
247 LOG_FATAL_IF(b->bundle < 0,
248 "TextOutput::popBundle() called more times than pushBundle()");
249 if (b->bundle < 0) b->bundle = 0;
250
251 if (b->bundle == 0) {
252 // Last bundle, write out data if it is complete. If it is not
253 // complete, don't write until the last line is done... this may
254 // or may not be the write thing to do, but it's the easiest.
255 if (b->bufferPos > 0 && b->atFront) {
256 struct iovec vec;
257 vec.iov_base = b->buffer;
258 vec.iov_len = b->bufferPos;
259 writeLines(vec, 1);
260 b->restart();
261 }
262 }
263}
264
265BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
266{
267 if ((mFlags&MULTITHREADED) != 0) {
268 ThreadState* ts = getThreadState();
269 if (ts) {
Yi Kongfdd8da92018-06-07 17:52:27 -0700270 while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800271 BufferState* bs = ts->states[mIndex].get();
Yi Kongfdd8da92018-06-07 17:52:27 -0700272 if (bs != nullptr && bs->seq == mSeq) return bs;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800273
274 ts->states.editItemAt(mIndex) = new BufferState(mIndex);
275 bs = ts->states[mIndex].get();
Yi Kongfdd8da92018-06-07 17:52:27 -0700276 if (bs != nullptr) return bs;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800277 }
278 }
279
280 return mGlobalState;
281}
282
283}; // namespace android