File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\store\PageOutputStream.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.store;
8
9 import org.h2.message.DbException;
10 import org.h2.message.Trace;
11 import org.h2.util.BitField;
12 import org.h2.util.IntArray;
13
14 /**
15 * An output stream that writes into a page store.
16 */
17 public class PageOutputStream {
18
19 private PageStore store;
20 private final Trace trace;
21 private final BitField exclude;
22 private final boolean atEnd;
23 private final int minPageId;
24
25 private int trunkPageId;
26 private int trunkNext;
27 private IntArray reservedPages = new IntArray();
28 private PageStreamTrunk trunk;
29 private int trunkIndex;
30 private PageStreamData data;
31 private int reserved;
32 private boolean needFlush;
33 private boolean writing;
34 private int pageCount;
35 private int logKey;
36
37 /**
38 * Create a new page output stream.
39 *
40 * @param store the page store
41 * @param trunkPage the first trunk page (already allocated)
42 * @param exclude the pages not to use
43 * @param logKey the log key of the first trunk page
44 * @param atEnd whether only pages at the end of the file should be used
45 */
46 public PageOutputStream(PageStore store, int trunkPage, BitField exclude, int logKey, boolean atEnd) {
47 this.trace = store.getTrace();
48 this.store = store;
49 this.trunkPageId = trunkPage;
50 this.exclude = exclude;
51 // minus one, because we increment before creating a trunk page
52 this.logKey = logKey - 1;
53 this.atEnd = atEnd;
54 minPageId = atEnd ? trunkPage : 0;
55 }
56
57 /**
58 * Allocate the required pages so that no pages need to be allocated while
59 * writing.
60 *
61 * @param minBuffer the number of bytes to allocate
62 */
63 void reserve(int minBuffer) {
64 if (reserved < minBuffer) {
65 int pageSize = store.getPageSize();
66 int capacityPerPage = PageStreamData.getCapacity(pageSize);
67 int pages = PageStreamTrunk.getPagesAddressed(pageSize);
68 int pagesToAllocate = 0, totalCapacity = 0;
69 do {
70 // allocate x data pages plus one trunk page
71 pagesToAllocate += pages + 1;
72 totalCapacity += pages * capacityPerPage;
73 } while (totalCapacity < minBuffer);
74 int firstPageToUse = atEnd ? trunkPageId : 0;
75 store.allocatePages(reservedPages, pagesToAllocate, exclude, firstPageToUse);
76 reserved += totalCapacity;
77 if (data == null) {
78 initNextData();
79 }
80 }
81 }
82
83 private void initNextData() {
84 int nextData = trunk == null ? -1 : trunk.getPageData(trunkIndex++);
85 if (nextData == -1) {
86 int parent = trunkPageId;
87 if (trunkNext != 0) {
88 trunkPageId = trunkNext;
89 }
90 int len = PageStreamTrunk.getPagesAddressed(store.getPageSize());
91 int[] pageIds = new int[len];
92 for (int i = 0; i < len; i++) {
93 pageIds[i] = reservedPages.get(i);
94 }
95 trunkNext = reservedPages.get(len);
96 logKey++;
97 trunk = PageStreamTrunk.create(store, parent, trunkPageId, trunkNext, logKey, pageIds);
98 trunkIndex = 0;
99 pageCount++;
100 trunk.write();
101 reservedPages.removeRange(0, len + 1);
102 nextData = trunk.getPageData(trunkIndex++);
103 }
104 data = PageStreamData.create(store, nextData, trunk.getPos(), logKey);
105 pageCount++;
106 data.initWrite();
107 }
108
109 /**
110 * Write the data.
111 *
112 * @param b the buffer
113 * @param off the offset
114 * @param len the length
115 */
116 public void write(byte[] b, int off, int len) {
117 if (len <= 0) {
118 return;
119 }
120 if (writing) {
121 DbException.throwInternalError("writing while still writing");
122 }
123 try {
124 reserve(len);
125 writing = true;
126 while (len > 0) {
127 int l = data.write(b, off, len);
128 if (l < len) {
129 storePage();
130 initNextData();
131 }
132 reserved -= l;
133 off += l;
134 len -= l;
135 }
136 needFlush = true;
137 } finally {
138 writing = false;
139 }
140 }
141
142 private void storePage() {
143 if (trace.isDebugEnabled()) {
144 trace.debug("pageOut.storePage " + data);
145 }
146 data.write();
147 }
148
149 /**
150 * Write all data.
151 */
152 public void flush() {
153 if (needFlush) {
154 storePage();
155 needFlush = false;
156 }
157 }
158
159 /**
160 * Close the stream.
161 */
162 public void close() {
163 store = null;
164 }
165
166 int getCurrentDataPageId() {
167 return data.getPos();
168 }
169
170 /**
171 * Fill the data page with zeros and write it.
172 * This is required for a checkpoint.
173 */
174 void fillPage() {
175 if (trace.isDebugEnabled()) {
176 trace.debug("pageOut.storePage fill " + data.getPos());
177 }
178 reserve(data.getRemaining() + 1);
179 reserved -= data.getRemaining();
180 data.write();
181 initNextData();
182 }
183
184 long getSize() {
185 return pageCount * store.getPageSize();
186 }
187
188 /**
189 * Remove a trunk page from the stream.
190 *
191 * @param t the trunk page
192 */
193 void free(PageStreamTrunk t) {
194 pageCount -= t.free(0);
195 }
196
197 /**
198 * Free up all reserved pages.
199 */
200 void freeReserved() {
201 if (reservedPages.size() > 0) {
202 int[] array = new int[reservedPages.size()];
203 reservedPages.toArray(array);
204 reservedPages = new IntArray();
205 reserved = 0;
206 for (int p : array) {
207 store.free(p, false);
208 }
209 }
210 }
211
212 /**
213 * Get the smallest possible page id used. This is the trunk page if only
214 * appending at the end of the file, or 0.
215 *
216 * @return the smallest possible page.
217 */
218 int getMinPageId() {
219 return minPageId;
220 }
221
222 }
223