Update project structure for aaf/cadi
[aaf/cadi.git] / core / src / main / java / org / onap / aaf / cadi / Capacitor.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aaf\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package org.onap.aaf.cadi;\r
24 \r
25 import java.nio.ByteBuffer;\r
26 import java.util.ArrayList;\r
27 \r
28 /**\r
29  * Capacitor\r
30  * \r
31  * Storage mechanism for read data, specifically designed for InputStreams.\r
32  * \r
33  * The Standard BufferedInputStream requires a limit to be set for buffered reading, which is \r
34  * impractical for reading SOAP headers, which can be quite large.\r
35  *\r
36  */\r
37 public class Capacitor {\r
38         private static final int DEFAULT_CHUNK = 256;\r
39         private ArrayList<ByteBuffer> bbs = new ArrayList<ByteBuffer>();\r
40         private ByteBuffer curr = null;\r
41         private int idx;\r
42         \r
43         // Maintain a private RingBuffer for Memory, for efficiency\r
44         private static ByteBuffer[] ring = new ByteBuffer[16];\r
45         private static int start, end;\r
46         \r
47         \r
48         public void put(byte b) {\r
49                 if(curr == null || curr.remaining()==0) { // ensure we have a "curr" buffer ready for data\r
50                         curr = ringGet();\r
51                         bbs.add(curr);\r
52                 }\r
53                 curr.put(b); \r
54         }\r
55 \r
56         public int read() {\r
57                 if(curr!=null) { \r
58                         if(curr.remaining()>0) { // have a buffer, use it!\r
59                                 return curr.get();\r
60                         } else if(idx<bbs.size()){ // Buffer not enough, get next one from array\r
61                                 if(idx<bbs.size()) {\r
62                                         curr=bbs.get(idx++);\r
63                                         return curr.get();\r
64                                 }\r
65                         }\r
66                 } // if no curr buffer, treat as end of stream\r
67                 return -1;\r
68         }\r
69         \r
70         /**\r
71          * read into an array like Streams\r
72          * \r
73          * @param array\r
74          * @param offset\r
75          * @param length\r
76          * @return\r
77          */\r
78         public int read(byte[] array, int offset, int length) {\r
79                 if(curr==null)return -1;\r
80                 int len;\r
81                 int count=0;\r
82                 while(length>0) { // loop through while there's data needed\r
83                         if((len=curr.remaining())>length) { //  if enough data in curr buffer, use this code\r
84                                 curr.get(array,offset,length);\r
85                                 count+=length;\r
86                                 length=0;\r
87                         } else {  // get data from curr, mark how much is needed to fulfil, and loop for next curr.\r
88                                 curr.get(array,offset,len);\r
89                                 count+=len;\r
90                                 offset+=len;\r
91                                 length-=len;\r
92                                 if(idx<bbs.size()) {\r
93                                         curr=bbs.get(idx++);\r
94                                 } else {\r
95                                         length=0; // stop, and return the count of how many we were able to load\r
96                                 }\r
97                         }\r
98                 }\r
99                 return count;\r
100         }\r
101 \r
102         /**\r
103          * Put an array of data into Capacitor\r
104          * \r
105          * @param array\r
106          * @param offset\r
107          * @param length\r
108          */\r
109         public void put(byte[] array, int offset, int length) {\r
110                 if(curr == null || curr.remaining()==0) {\r
111                         curr = ringGet();\r
112                         bbs.add(curr);\r
113                 }\r
114                 \r
115                 int len;\r
116                 while(length>0) {\r
117                         if((len=curr.remaining())>length) {\r
118                                 curr.put(array,offset,length);\r
119                                 length=0;\r
120                         } else {\r
121 //                              System.out.println(new String(array));\r
122                                 curr.put(array,offset,len);\r
123                                 length-=len;\r
124                                 offset+=len;\r
125                                 curr = ringGet();\r
126                                 bbs.add(curr);\r
127                         }\r
128                 }\r
129         }\r
130          \r
131         /**\r
132          * Move state from Storage mode into Read mode, changing all internal buffers to read mode, etc\r
133          */\r
134         public void setForRead() {\r
135                 for(ByteBuffer bb : bbs) {\r
136                         bb.flip();\r
137                 }\r
138                 if(bbs.isEmpty()) {\r
139                         curr = null;\r
140                         idx = 0;\r
141                 } else {\r
142                         curr=bbs.get(0);\r
143                         idx=1;\r
144                 }\r
145         }\r
146         \r
147         /**\r
148          * reuse all the buffers\r
149          */\r
150         public void done() {\r
151                 for(ByteBuffer bb : bbs) {\r
152                         ringPut(bb);\r
153                 }\r
154                 bbs.clear();\r
155                 curr = null;\r
156         }\r
157         \r
158         /**\r
159          * Declare amount of data available to be read at once.\r
160          * \r
161          * @return\r
162          */\r
163         public int available() {\r
164                 int count = 0;\r
165                 for(ByteBuffer bb : bbs) {\r
166                         count+=bb.remaining();\r
167                 }\r
168                 return count;\r
169         }\r
170         \r
171         /**\r
172          * Returns how many are left that were not skipped\r
173          * @param n\r
174          * @return\r
175          */\r
176         public long skip(long n) {\r
177                 long skipped=0L;\r
178                 int skip;\r
179                 while(n>0) {\r
180                         if(n<(skip=curr.remaining())) {\r
181                                 curr.position(curr.position()+(int)n);\r
182                                 skipped+=skip;\r
183                                 n=0;\r
184                         } else {\r
185                                 curr.position(curr.limit());\r
186                                 \r
187                                 skipped-=skip;\r
188                                 if(idx<bbs.size()) {\r
189                                         curr=bbs.get(idx++);\r
190                                         n-=skip;\r
191                                 } else {\r
192                                         n=0;\r
193                                 }\r
194                         }\r
195                 }\r
196                 return skipped;\r
197         }\r
198         /**\r
199          * Be able to re-read data that is stored that has already been re-read.  This is not a standard Stream behavior, but can be useful\r
200          * in a standalone mode.\r
201          */\r
202         public void reset() {\r
203                 for(ByteBuffer bb : bbs) {\r
204                         bb.position(0);\r
205                 }\r
206                 if(bbs.isEmpty()) {\r
207                         curr = null;\r
208                         idx = 0;\r
209                 } else {\r
210                         curr=bbs.get(0);\r
211                         idx=1;\r
212                 }\r
213         }\r
214 \r
215         /*\r
216          * Ring Functions.  Reuse allocated memory \r
217          */\r
218         private ByteBuffer ringGet() {\r
219                 ByteBuffer bb = null;\r
220                 synchronized(ring) {\r
221                         bb=ring[start];\r
222                         ring[start]=null;\r
223                         if(bb!=null && ++start>15)start=0;\r
224                 }\r
225                 if(bb==null) {\r
226                         bb=ByteBuffer.allocate(DEFAULT_CHUNK);\r
227                 } else {\r
228                         bb.clear();// refresh reused buffer\r
229                 }\r
230                 return bb;\r
231         }\r
232         \r
233         private void ringPut(ByteBuffer bb) {\r
234                 synchronized(ring) {\r
235                         ring[end]=bb; // if null or not, BB will just be Garbage collected\r
236                         if(++end>15)end=0;\r
237                 }\r
238         }\r
239 \r
240 }\r