1 /*
2  * Copyright (C) 2017 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 
17 package android.location.cts.asn1.base;
18 
19 import com.google.common.base.Preconditions;
20 import com.google.common.collect.ImmutableList;
21 
22 import java.nio.ByteBuffer;
23 import java.util.Collection;
24 import java.util.LinkedList;
25 
26 /**
27  * Implements ASN.1 functionality.
28  *
29  */
30 public abstract class Asn1SequenceOf<T extends Asn1Object> extends Asn1Object {
31   private static final Collection<Asn1Tag> possibleFirstTags =
32       ImmutableList.of(Asn1Tag.SEQUENCE);
33 
34   protected LinkedList<T> sequence = new LinkedList<T>();
35   private int minimumSize = 0;
36   private Integer maximumSize = null; // Null is unbounded.
37 
getPossibleFirstTags()38   public static Collection<Asn1Tag> getPossibleFirstTags() {
39     return possibleFirstTags;
40   }
41 
setMinSize(int min)42   protected void setMinSize(int min) {
43     minimumSize = min;
44   }
45 
setMaxSize(int max)46   protected void setMaxSize(int max) {
47     maximumSize = max;
48   }
49 
add(T component)50   public void add(T component) {
51     sequence.addLast(component);
52   }
53 
getValues()54   public Iterable<T> getValues() {
55     return sequence;
56   }
57 
createAndAddValue()58   public abstract T createAndAddValue();
59 
getDefaultTag()60   @Override Asn1Tag getDefaultTag() {
61     return Asn1Tag.SEQUENCE;
62   }
63 
isConstructed()64   @Override boolean isConstructed() {
65     return true;
66   }
67 
getBerValueLength()68   @Override int getBerValueLength() {
69     int length = 0;
70     for (Asn1Object component : sequence) {
71       length += component.getBerLength();
72     }
73     return length;
74   }
75 
encodeBerValue(ByteBuffer buf)76   @Override void encodeBerValue(ByteBuffer buf) {
77     for (Asn1Object component : sequence) {
78       component.encodeBer(buf);
79     }
80   }
81 
decodeBerValue(ByteBuffer buf)82   @Override void decodeBerValue(ByteBuffer buf) {
83     while (buf.hasRemaining()) {
84       Asn1Tag tag = Asn1Tag.readTag(buf);
85       int valueLength = Asn1Tag.readLength(buf);
86       T value = createAndAddValue();
87       if (value.getTag() != null) {
88         checkTag(tag, value.getTag());
89         if (!value.isTagImplicit()) {
90           // read inner tag + length
91           checkTag(value.getDefaultTag(), Asn1Tag.readTag(buf));
92           valueLength = Asn1Tag.readLength(buf);
93         }
94       } else {
95         checkTag(tag, value.getDefaultTag());
96       }
97       ByteBuffer subBuf = ByteBuffer.wrap(buf.array(), buf.position(), valueLength);
98       value.decodeBerValue(subBuf);
99       if (subBuf.hasRemaining()) {
100         throw new IllegalArgumentException("child failed to consume all input");
101       }
102       buf.position(buf.position() + valueLength);
103     }
104   }
105 
encodePerImpl(boolean aligned)106   private Iterable<BitStream> encodePerImpl(boolean aligned) {
107     Preconditions.checkState(sequence.size() >= minimumSize,
108                              "Too few components.");
109     Preconditions.checkState(maximumSize == null
110                              || sequence.size() <= maximumSize,
111                              "Too many components.");
112     ImmutableList.Builder<BitStream> listBuilder = ImmutableList.builder();
113     if (maximumSize == null || maximumSize >= PerAlignedUtils.SIXTYFOUR_K) {
114       if (aligned) {
115         listBuilder.add(PerAlignedUtils.encodeSemiConstrainedLength(sequence.size()));
116       } else {
117         listBuilder.add(PerUnalignedUtils.encodeSemiConstrainedLength(sequence.size()));
118       }
119     } else if (maximumSize != minimumSize) {
120       if (aligned) {
121         listBuilder.add(
122             PerAlignedUtils.encodeSmallConstrainedWholeNumber(
123                 sequence.size(), minimumSize, maximumSize));
124       } else {
125         listBuilder.add(
126             PerUnalignedUtils.encodeConstrainedWholeNumber(
127                 sequence.size(), minimumSize, maximumSize));
128       }
129     }
130     for (Asn1Object component : sequence) {
131       if (aligned) {
132         listBuilder.addAll(component.encodePerAligned());
133       } else {
134         listBuilder.addAll(component.encodePerUnaligned());
135       }
136     }
137     return listBuilder.build();
138   }
139 
encodePerUnaligned()140   @Override public Iterable<BitStream> encodePerUnaligned() {
141     return encodePerImpl(false);
142   }
143 
encodePerAligned()144   @Override public Iterable<BitStream> encodePerAligned() {
145     return encodePerImpl(true);
146   }
147 
decodePerImpl(BitStreamReader reader, boolean aligned)148   private void decodePerImpl(BitStreamReader reader, boolean aligned) {
149     int size = minimumSize;
150     if (maximumSize == null || maximumSize >= PerAlignedUtils.SIXTYFOUR_K) {
151       if (aligned) {
152         size = PerAlignedUtils.decodeSemiConstrainedLength(reader);
153       } else {
154         size = PerUnalignedUtils.decodeSemiConstrainedLength(reader);
155       }
156     } else if (maximumSize != minimumSize) {
157       if (aligned) {
158         size = PerAlignedUtils.decodeSmallConstrainedWholeNumber(
159             reader, minimumSize, maximumSize);
160       } else {
161         size = PerUnalignedUtils.decodeConstrainedWholeNumber(
162             reader, minimumSize, maximumSize);
163       }
164     }
165     for (int i = 0; i < size; i++) {
166       T value = createAndAddValue();
167       if (aligned) {
168         value.decodePerAligned(reader);
169       } else {
170         value.decodePerUnaligned(reader);
171       }
172     }
173   }
174 
decodePerUnaligned(BitStreamReader reader)175   @Override public void decodePerUnaligned(BitStreamReader reader) {
176     decodePerImpl(reader, false);
177   }
178 
decodePerAligned(BitStreamReader reader)179   @Override public void decodePerAligned(BitStreamReader reader) {
180     decodePerImpl(reader, true);
181   }
182 }
183