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