4 * Abstract Syntax Notation 1 Encoding Rules
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.90 2004/07/12 03:42:22 csoutheren
28 * Fixed problem with checking character set constraints too aggressively
30 * Revision 1.89 2004/07/12 01:56:10 csoutheren
31 * Fixed incorrect asn decoding check
33 * Revision 1.88 2004/07/11 14:19:07 csoutheren
34 * More bulletproofing of ASN routines against random data attacks
36 * Revision 1.87 2004/07/11 12:33:47 csoutheren
37 * Added guards against illegal PDU values causing crashes
39 * Revision 1.86 2004/04/22 07:54:01 csoutheren
40 * Fix problem with VS.net asserting on in isprint when chars outside normal range
42 * Revision 1.85 2004/04/18 04:33:37 rjongbloed
43 * Changed all operators that return BOOL to return standard type bool. This is primarily
44 * for improved compatibility with std STL usage removing many warnings.
46 * Revision 1.84 2004/04/03 08:22:20 csoutheren
47 * Remove pseudo-RTTI and replaced with real RTTI
49 * Revision 1.83 2004/01/17 09:21:21 csoutheren
50 * Added protection against NULL ptr to PASN_Stream::BlockDecode
52 * Revision 1.82 2003/08/01 02:11:38 csoutheren
53 * Changed to allow easy isolation of PER, BER and XER encoding/decoding routines
55 * Revision 1.81 2003/04/28 02:50:33 robertj
56 * Fixed problem with spaces in type name, thanks Federico Pinna
58 * Revision 1.80 2003/02/26 04:37:21 robertj
59 * Tidied some comments
61 * Revision 1.79 2003/02/26 01:57:44 robertj
62 * Added XML encoding rules to ASN system, thanks Federico Pinna
64 * Revision 1.78 2003/01/24 23:43:43 robertj
65 * Fixed subtle problems with the use of MAX keyword for unsigned numbers,
66 * should beUINT_MAX not INT_MAX, thanks Stevie Gray for pointing it out.
68 * Revision 1.77 2002/12/17 07:00:15 robertj
69 * Fixed incorrect encoding of arrays greater than 8192 and less than 16384
71 * Revision 1.76 2002/12/13 03:57:17 robertj
72 * Fixed crash if enable extension fields beyond the "known" extensions.
74 * Revision 1.75 2002/12/02 01:03:33 robertj
75 * Fixed bug were if setting the size of a constrained bit string, it
76 * actually sets the size of the underlying byte array correctly.
78 * Revision 1.74 2002/11/26 23:29:32 robertj
79 * Added missing const to DecodeSubType() function.
81 * Revision 1.73 2002/11/22 09:43:32 robertj
82 * Fixed encoding of a ASN NULL sequence extension field, eg fastConnectRefused
84 * Revision 1.72 2002/11/21 03:46:22 robertj
85 * Changed to encode only the minimum number of bits required, this improves
86 * compatibility with some brain dead ASN decoders.
88 * Revision 1.71 2002/11/06 22:47:24 robertj
89 * Fixed header comment (copyright etc)
91 * Revision 1.70 2002/10/31 05:51:10 robertj
92 * Changed to use new UTF-8/UCS-2 conversion functions on PString.
94 * Revision 1.69 2002/10/29 08:12:44 robertj
95 * Fixed MSVC warnings.
97 * Revision 1.68 2002/10/29 07:26:45 robertj
98 * Fixed subtle bug when encoding or decoding Octet String with 1 or 2 bytes
99 * in it, was not byte aligned correctly.
101 * Revision 1.67 2002/09/26 23:53:20 robertj
102 * Fixed incorrect asserts in PASN_Enumerated, thanks Platzer Wolfgang
104 * Revision 1.66 2002/09/13 08:16:15 robertj
105 * Fixed missing line feed when dumping hex octet strings.
107 * Revision 1.65 2002/08/06 02:27:58 robertj
108 * GNU C++ v3 compatibility.
110 * Revision 1.64 2002/07/25 10:52:49 robertj
111 * Changes to allow more granularity in PDU dumps, hex output increasing
112 * with increasing trace level.
114 * Revision 1.63 2002/06/05 12:29:15 craigs
115 * Changes for gcc 3.1
117 * Revision 1.62 2002/05/29 01:22:35 robertj
118 * Added ability to set object id from unsigned integer arrays.
120 * Revision 1.61 2002/05/21 04:23:40 robertj
121 * Fixed problem with ASN encoding/decoding unsconstrained negative numbers,
123 * Revision 1.60 2002/05/14 08:34:29 robertj
124 * Fixed problem encoding unsigned where value==lower bound, thanks Greg Adams.
126 * Revision 1.59 2002/05/14 06:59:50 robertj
127 * Added more bullet proofing so a malformed PDU cannot cause teh decoder
128 * to try and allocate huge arrays and consume all CPU and memory on a
129 * system. A configurable limit of 100 is set for things like SEQUENCE OF.
131 * Revision 1.58 2002/02/08 12:47:19 robertj
132 * Fixed incorrect encoding of integer, did not allow for sign bit, thanks Kevin Tran.
134 * Revision 1.57 2002/02/01 01:17:36 robertj
135 * Fixed bug in encoding empty strings (H.450 issue), thanks Frans Dams, Frank Derks et al.
137 * Revision 1.56 2002/01/30 08:40:55 robertj
138 * Fixed incorrect decode function in BER string decode, thanks ct_dev@sohu.com
140 * Revision 1.55 2001/12/13 09:13:57 robertj
141 * Added function get get oid as a string.
143 * Revision 1.54 2001/11/26 03:07:13 robertj
144 * Fixed decode of extendable constrained integer types.
146 * Revision 1.53 2001/09/14 05:26:11 robertj
147 * Fixed problem with assigning a PASN_Choice to itself, thanks Chih-Wei Huang
149 * Revision 1.52 2001/09/14 01:59:59 robertj
150 * Fixed problem with incorrectly initialised PASN_Choice sub-object.
152 * Revision 1.51 2001/08/08 04:19:28 robertj
153 * Fixed PString<->BMPString conversion so can have embedded nulls.
155 * Revision 1.50 2001/08/07 04:37:03 robertj
156 * Simplified &#num; parsing.
158 * Revision 1.49 2001/08/07 02:49:05 robertj
159 * Fixed incorrect alignment if constrained string upper bound is exactly
160 * 16 bits long. thanks Guntram Diehl & Thomas Arimont.
162 * Revision 1.48 2001/08/06 09:35:25 robertj
163 * Fixed GNU compatibility.
165 * Revision 1.47 2001/08/06 09:31:48 robertj
166 * Added conversion of BMPString to PString without losing special characters.
168 * Revision 1.46 2001/08/06 01:39:02 robertj
169 * Added assignement operator with RHS of PASN_BMPString to classes
170 * descended from PASN_BMPString.
172 * Revision 1.45 2001/06/14 02:14:12 robertj
173 * Added functions to encode and decode another ASN type that is inside
174 * an octet string, useful for ANY or EXTERNAL types etc.
176 * Revision 1.44 2001/05/29 00:59:16 robertj
177 * Fixed excessive padding on constrained strings.
179 * Revision 1.43 2001/05/22 23:37:42 robertj
180 * Fixed problem with assigning a constrained string value to itself, which
181 * can occur when changing constraints.
183 * Revision 1.42 2001/04/30 10:47:33 robertj
184 * Fixed stupid error in last patch.
186 * Revision 1.41 2001/04/30 06:47:04 robertj
187 * Fixed problem with en/decoding more than 16 extension fields in a sequence.
189 * Revision 1.40 2001/04/26 08:15:58 robertj
190 * Fixed problem with ASN compile of single constraints on enumerations.
192 * Revision 1.39 2001/04/23 05:46:06 robertj
193 * Fixed problem with unconstrained PASN_NumericString coding in 8 bits
194 * instead of 4, thanks Chew Kuan.
196 * Revision 1.38 2001/04/23 04:40:14 robertj
197 * Added ASN standard types GeneralizedTime and UTCTime
199 * Revision 1.37 2001/04/12 03:26:59 robertj
200 * Fixed PASN_Boolean cosntructor to be compatible with usage in ASN parser.
201 * Changed all PASN_xxx types so constructor can take real type as only
202 * parameter. eg PASN_OctetString s = "fred";
203 * Changed block encode/decode so does not do a ByteAlign() if zero
204 * length, required for interoperability even though spec implies otherwise..
206 * Revision 1.36 2001/01/24 04:37:07 robertj
207 * Added more bulletproofing to ASN structures to obey constraints.
209 * Revision 1.35 2001/01/03 01:20:13 robertj
210 * Fixed error in BlockEncode, should ByteAlign() even on zero length strings.
212 * Revision 1.34 2000/10/26 11:09:16 robertj
213 * More bullet proofing of PER decoder, changed bit type to be unsigned.
215 * Revision 1.33 2000/10/26 01:29:32 robertj
216 * Fixed MSVC warning.
218 * Revision 1.32 2000/10/25 04:05:38 robertj
219 * More bullet proofing of PER decoder.
221 * Revision 1.31 2000/09/29 04:11:51 robertj
222 * Fixed possible out of range memory access, thanks Petr Parýzek <paryzek@wo.cz>
224 * Revision 1.30 2000/02/29 06:32:12 robertj
225 * Added ability to remove optional field in sequence, thanks Dave Harvey.
227 * Revision 1.29 2000/01/20 06:22:22 robertj
228 * Fixed boundary condition error for constrained integer encoding (values 1, 256 etc)
230 * Revision 1.28 1999/11/22 23:15:43 robertj
231 * Fixed bug in PASN_Choice::Compare(), should make sure choices are the same before comparing.
233 * Revision 1.27 1999/08/19 15:43:07 robertj
234 * Fixed incorrect size of OID if zero length encoded.
236 * Revision 1.26 1999/08/09 13:02:45 robertj
237 * dded ASN compiler #defines for backward support of pre GCC 2.9 compilers.
238 * Added ASN compiler #defines to reduce its memory footprint.
240 * Revision 1.25 1999/08/08 15:45:59 robertj
241 * Fixed incorrect encoding of unknown extensions.
243 * Revision 1.24 1999/08/05 00:44:28 robertj
244 * Fixed PER encoding problems for large integer values.
246 * Revision 1.23 1999/07/22 06:48:54 robertj
247 * Added comparison operation to base ASN classes and compiled ASN code.
248 * Added support for ANY type in ASN parser.
250 * Revision 1.22 1999/07/08 08:39:12 robertj
251 * Fixed bug when assigning negative number ot cosntrained PASN_Integer
253 * Revision 1.21 1999/06/30 08:58:12 robertj
254 * Fixed bug in encoding/decoding OID greater than 2.39
256 * Revision 1.20 1999/06/17 13:27:09 robertj
257 * Fixed bug causing crashes on pass through of unknown extensions.
259 * Revision 1.19 1999/06/07 00:31:25 robertj
260 * Fixed signed/unsigned problem with number of unknown extensions check.
262 * Revision 1.18 1999/04/26 05:58:48 craigs
263 * Fixed problems with encoding of extensions
265 * Revision 1.17 1999/03/09 08:12:38 robertj
266 * Fixed problem with closing a steam encoding twice.
268 * Revision 1.16 1999/01/16 01:28:25 robertj
269 * Fixed problems with reading stream multiple times.
271 * Revision 1.15 1998/11/30 04:50:44 robertj
272 * New directory structure
274 * Revision 1.14 1998/10/22 04:33:11 robertj
275 * Fixed bug in constrained strings and PER, incorrect order of character set.
277 * Revision 1.13 1998/09/23 06:21:49 robertj
278 * Added open source copyright license.
280 * Revision 1.12 1998/05/26 05:29:23 robertj
281 * Workaroung for g++ iostream bug.
283 * Revision 1.11 1998/05/21 04:58:54 robertj
286 * Revision 1.10 1998/05/21 04:26:54 robertj
287 * Fixed numerous PER problems.
289 * Revision 1.9 1998/05/11 06:01:55 robertj
290 * Why did this compile under MSC?
292 * Revision 1.8 1998/05/07 05:19:29 robertj
293 * Fixed problems with using copy constructor/assignment oeprator on PASN_Objects.
295 * Revision 1.7 1998/03/05 12:49:50 robertj
298 * Revision 1.6 1998/02/03 06:28:27 robertj
299 * Fixed length calculation of integers in BER.
300 * Added new function to read a block with minimum number of bytes.
302 * Revision 1.5 1998/01/26 01:51:20 robertj
303 * Removed uninitialised variable warnings.
305 * Revision 1.4 1997/12/18 05:07:56 robertj
306 * Fixed bug in choice name display.
307 * Added function to get choice discriminator name.
308 * Fixed bug in encoding extensions.
310 * Revision 1.3 1997/12/11 10:36:22 robertj
311 * Support for new ASN parser.
318 #pragma implementation "asner.h"
321 #include <ptclib/asner.h>
324 #include <ptclib/pxml.h>
330 static PINDEX MaximumArraySize
= 128;
331 static PINDEX MaximumStringSize
= 16*1024;
332 static PINDEX MaximumSetSize
= 512;
335 static PINDEX
CountBits(unsigned range
)
339 return sizeof(unsigned)*8;
345 while (nBits
< (sizeof(unsigned)*8) && range
> (unsigned)(1 << nBits
))
350 inline BOOL
CheckByteOffset(PINDEX offset
, PINDEX upper
= MaximumStringSize
)
352 // a 1mbit PDU has got to be an error
353 return (0 <= offset
&& offset
<= upper
);
357 ///////////////////////////////////////////////////////////////////////
359 PASN_Object::PASN_Object(unsigned theTag
, TagClass theTagClass
, BOOL extend
)
365 if (theTagClass
!= DefaultTagClass
)
366 tagClass
= theTagClass
;
368 tagClass
= ContextSpecificTagClass
;
372 void PASN_Object::SetTag(unsigned newTag
, TagClass tagClass_
)
375 if (tagClass_
!= DefaultTagClass
)
376 tagClass
= tagClass_
;
380 PINDEX
PASN_Object::GetObjectLength() const
385 len
+= (CountBits(tag
)+6)/7;
387 PINDEX dataLen
= GetDataLength();
391 len
+= (CountBits(dataLen
)+7)/8 + 1;
393 return len
+ dataLen
;
397 void PASN_Object::SetConstraintBounds(ConstraintType
, int, unsigned)
402 void PASN_Object::SetCharacterSet(ConstraintType
, const char *)
407 void PASN_Object::SetCharacterSet(ConstraintType
, unsigned, unsigned)
412 PINDEX
PASN_Object::GetMaximumArraySize()
414 return MaximumArraySize
;
418 void PASN_Object::SetMaximumArraySize(PINDEX sz
)
420 MaximumArraySize
= sz
;
424 PINDEX
PASN_Object::GetMaximumStringSize()
426 return MaximumStringSize
;
430 void PASN_Object::SetMaximumStringSize(PINDEX sz
)
432 MaximumStringSize
= sz
;
436 ///////////////////////////////////////////////////////////////////////
438 PASN_ConstrainedObject::PASN_ConstrainedObject(unsigned tag
, TagClass tagClass
)
439 : PASN_Object(tag
, tagClass
)
441 constraint
= Unconstrained
;
443 upperLimit
= UINT_MAX
;
447 void PASN_ConstrainedObject::SetConstraintBounds(ConstraintType ctype
,
448 int lower
, unsigned upper
)
451 if (constraint
== Unconstrained
) {
456 extendable
= ctype
== ExtendableConstraint
;
457 // if ((lower >= 0 && upper < 0x7fffffff) ||
458 // (lower < 0 && (unsigned)lower <= upper)) {
465 ///////////////////////////////////////////////////////////////////////
467 PASN_Null::PASN_Null(unsigned tag
, TagClass tagClass
)
468 : PASN_Object(tag
, tagClass
)
473 PObject::Comparison
PASN_Null::Compare(const PObject
& obj
) const
475 PAssert(PIsDescendant(&obj
, PASN_Null
), PInvalidCast
);
480 PObject
* PASN_Null::Clone() const
482 PAssert(IsClass(PASN_Null::Class()), PInvalidCast
);
483 return new PASN_Null(*this);
487 void PASN_Null::PrintOn(ostream
& strm
) const
493 PString
PASN_Null::GetTypeAsString() const
499 PINDEX
PASN_Null::GetDataLength() const
505 BOOL
PASN_Null::Decode(PASN_Stream
& strm
)
507 return strm
.NullDecode(*this);
511 void PASN_Null::Encode(PASN_Stream
& strm
) const
513 strm
.NullEncode(*this);
517 ///////////////////////////////////////////////////////////////////////
519 PASN_Boolean::PASN_Boolean(BOOL val
)
520 : PASN_Object(UniversalBoolean
, UniversalTagClass
)
526 PASN_Boolean::PASN_Boolean(unsigned tag
, TagClass tagClass
, BOOL val
)
527 : PASN_Object(tag
, tagClass
)
533 PObject::Comparison
PASN_Boolean::Compare(const PObject
& obj
) const
535 PAssert(PIsDescendant(&obj
, PASN_Boolean
), PInvalidCast
);
536 return value
== ((const PASN_Boolean
&)obj
).value
? EqualTo
: GreaterThan
;
540 PObject
* PASN_Boolean::Clone() const
542 PAssert(IsClass(PASN_Boolean::Class()), PInvalidCast
);
543 return new PASN_Boolean(*this);
547 void PASN_Boolean::PrintOn(ostream
& strm
) const
556 PString
PASN_Boolean::GetTypeAsString() const
562 PINDEX
PASN_Boolean::GetDataLength() const
568 BOOL
PASN_Boolean::Decode(PASN_Stream
& strm
)
570 return strm
.BooleanDecode(*this);
574 void PASN_Boolean::Encode(PASN_Stream
& strm
) const
576 strm
.BooleanEncode(*this);
580 ///////////////////////////////////////////////////////////////////////
582 PASN_Integer::PASN_Integer(unsigned val
)
583 : PASN_ConstrainedObject(UniversalInteger
, UniversalTagClass
)
589 PASN_Integer::PASN_Integer(unsigned tag
, TagClass tagClass
, unsigned val
)
590 : PASN_ConstrainedObject(tag
, tagClass
)
596 BOOL
PASN_Integer::IsUnsigned() const
598 return constraint
!= Unconstrained
&& lowerLimit
>= 0;
602 PASN_Integer
& PASN_Integer::operator=(unsigned val
)
604 if (constraint
== Unconstrained
)
606 else if (lowerLimit
>= 0) { // Is unsigned integer
607 if (val
< (unsigned)lowerLimit
)
609 else if (val
> upperLimit
)
616 if (ival
< lowerLimit
)
618 else if (upperLimit
< INT_MAX
&& ival
> (int)upperLimit
)
628 PObject::Comparison
PASN_Integer::Compare(const PObject
& obj
) const
630 PAssert(PIsDescendant(&obj
, PASN_Integer
), PInvalidCast
);
631 const PASN_Integer
& other
= (const PASN_Integer
&)obj
;
634 if (value
< other
.value
)
636 if (value
> other
.value
)
640 if ((int)value
< (int)other
.value
)
642 if ((int)value
> (int)other
.value
)
649 PObject
* PASN_Integer::Clone() const
651 PAssert(IsClass(PASN_Integer::Class()), PInvalidCast
);
652 return new PASN_Integer(*this);
656 void PASN_Integer::PrintOn(ostream
& strm
) const
658 if (constraint
== Unconstrained
|| lowerLimit
< 0)
665 void PASN_Integer::SetConstraintBounds(ConstraintType type
, int lower
, unsigned upper
)
667 PASN_ConstrainedObject::SetConstraintBounds(type
, lower
, upper
);
672 PString
PASN_Integer::GetTypeAsString() const
678 static PINDEX
GetIntegerDataLength(int value
)
680 // create a mask which is the top nine bits of a DWORD, or 0xFF800000
681 // on a big endian machine
682 int shift
= (sizeof(value
)-1)*8-1;
684 // remove all sequences of nine 0's or 1's at the start of the value
685 while (shift
> 0 && ((value
>> shift
)&0x1ff) == (value
< 0 ? 0x1ff : 0))
692 PINDEX
PASN_Integer::GetDataLength() const
694 return GetIntegerDataLength(value
);
698 BOOL
PASN_Integer::Decode(PASN_Stream
& strm
)
700 return strm
.IntegerDecode(*this);
704 void PASN_Integer::Encode(PASN_Stream
& strm
) const
706 strm
.IntegerEncode(*this);
710 ///////////////////////////////////////////////////////////////////////
712 PASN_Enumeration::PASN_Enumeration(unsigned val
)
713 : PASN_Object(UniversalEnumeration
, UniversalTagClass
, FALSE
)
716 maxEnumValue
= P_MAX_INDEX
;
720 PASN_Enumeration::PASN_Enumeration(unsigned tag
, TagClass tagClass
,
721 unsigned maxEnum
, BOOL extend
,
723 : PASN_Object(tag
, tagClass
, extend
)
726 maxEnumValue
= maxEnum
;
730 static POrdinalToString
BuildNamesDict(const PString
& nameSpec
)
732 POrdinalToString names
;
734 PStringArray nameList
= nameSpec
.Tokenise(' ', FALSE
);
737 for (PINDEX i
= 0; i
< nameList
.GetSize(); i
++) {
738 const PString
& thisName
= nameList
[i
];
740 PINDEX equalPos
= thisName
.Find('=');
741 if (equalPos
!= P_MAX_INDEX
)
742 num
= (int)thisName
.Mid(equalPos
+1).AsInteger();
743 names
.SetAt(POrdinalKey(num
), thisName
.Left(equalPos
));
752 PASN_Enumeration::PASN_Enumeration(unsigned tag
, TagClass tagClass
,
753 unsigned maxEnum
, BOOL extend
,
754 const PString
& nameSpec
,
756 : PASN_Object(tag
, tagClass
, extend
),
757 names(BuildNamesDict(nameSpec
))
759 maxEnumValue
= maxEnum
;
761 PAssert(val
<= maxEnum
, PInvalidParameter
);
766 PObject::Comparison
PASN_Enumeration::Compare(const PObject
& obj
) const
768 PAssert(PIsDescendant(&obj
, PASN_Enumeration
), PInvalidCast
);
769 const PASN_Enumeration
& other
= (const PASN_Enumeration
&)obj
;
771 if (value
< other
.value
)
774 if (value
> other
.value
)
781 PObject
* PASN_Enumeration::Clone() const
783 PAssert(IsClass(PASN_Enumeration::Class()), PInvalidCast
);
784 return new PASN_Enumeration(*this);
788 void PASN_Enumeration::PrintOn(ostream
& strm
) const
790 if (names
.Contains(value
))
791 strm
<< names
[value
];
793 strm
<< '<' << value
<< '>';
797 PString
PASN_Enumeration::GetTypeAsString() const
799 return "Enumeration";
803 PINDEX
PASN_Enumeration::GetDataLength() const
805 return GetIntegerDataLength(value
);
809 BOOL
PASN_Enumeration::Decode(PASN_Stream
& strm
)
811 return strm
.EnumerationDecode(*this);
815 void PASN_Enumeration::Encode(PASN_Stream
& strm
) const
817 strm
.EnumerationEncode(*this);
821 ///////////////////////////////////////////////////////////////////////
823 PASN_Real::PASN_Real(double val
)
824 : PASN_Object(UniversalReal
, UniversalTagClass
)
830 PASN_Real::PASN_Real(unsigned tag
, TagClass tagClass
, double val
)
831 : PASN_Object(tag
, tagClass
)
837 PObject::Comparison
PASN_Real::Compare(const PObject
& obj
) const
839 PAssert(PIsDescendant(&obj
, PASN_Real
), PInvalidCast
);
840 const PASN_Real
& other
= (const PASN_Real
&)obj
;
842 if (value
< other
.value
)
845 if (value
> other
.value
)
852 PObject
* PASN_Real::Clone() const
854 PAssert(IsClass(PASN_Real::Class()), PInvalidCast
);
855 return new PASN_Real(*this);
859 void PASN_Real::PrintOn(ostream
& strm
) const
865 PString
PASN_Real::GetTypeAsString() const
871 PINDEX
PASN_Real::GetDataLength() const
873 PAssertAlways(PUnimplementedFunction
);
878 BOOL
PASN_Real::Decode(PASN_Stream
& strm
)
880 return strm
.RealDecode(*this);
884 void PASN_Real::Encode(PASN_Stream
& strm
) const
886 strm
.RealEncode(*this);
890 ///////////////////////////////////////////////////////////////////////
892 PASN_ObjectId::PASN_ObjectId(const char * dotstr
)
893 : PASN_Object(UniversalObjectId
, UniversalTagClass
)
900 PASN_ObjectId::PASN_ObjectId(unsigned tag
, TagClass tagClass
)
901 : PASN_Object(tag
, tagClass
)
906 PASN_ObjectId::PASN_ObjectId(const PASN_ObjectId
& other
)
907 : PASN_Object(other
),
908 value(other
.value
, other
.GetSize())
913 PASN_ObjectId
& PASN_ObjectId::operator=(const PASN_ObjectId
& other
)
915 PASN_Object::operator=(other
);
916 value
= PUnsignedArray(other
.value
, other
.GetSize());
921 PASN_ObjectId
& PASN_ObjectId::operator=(const char * dotstr
)
931 PASN_ObjectId
& PASN_ObjectId::operator=(const PString
& dotstr
)
938 PASN_ObjectId
& PASN_ObjectId::operator=(const PUnsignedArray
& numbers
)
945 void PASN_ObjectId::SetValue(const PString
& dotstr
)
947 PStringArray parts
= dotstr
.Tokenise('.');
948 value
.SetSize(parts
.GetSize());
949 for (PINDEX i
= 0; i
< parts
.GetSize(); i
++)
950 value
[i
] = parts
[i
].AsUnsigned();
954 void PASN_ObjectId::SetValue(const unsigned * numbers
, PINDEX size
)
956 value
= PUnsignedArray(numbers
, size
);
960 bool PASN_ObjectId::operator==(const char * dotstr
) const
968 PObject::Comparison
PASN_ObjectId::Compare(const PObject
& obj
) const
970 PAssert(PIsDescendant(&obj
, PASN_ObjectId
), PInvalidCast
);
971 const PASN_ObjectId
& other
= (const PASN_ObjectId
&)obj
;
972 return value
.Compare(other
.value
);
976 PObject
* PASN_ObjectId::Clone() const
978 PAssert(IsClass(PASN_ObjectId::Class()), PInvalidCast
);
979 return new PASN_ObjectId(*this);
983 void PASN_ObjectId::PrintOn(ostream
& strm
) const
985 for (PINDEX i
= 0; i
< value
.GetSize(); i
++) {
986 strm
<< (unsigned)value
[i
];
987 if (i
< value
.GetSize()-1)
993 PString
PASN_ObjectId::AsString() const
1001 PString
PASN_ObjectId::GetTypeAsString() const
1007 BOOL
PASN_ObjectId::CommonDecode(PASN_Stream
& strm
, unsigned dataLen
)
1011 // handle zero length strings correctly
1017 // start at the second identifier in the buffer, because we will later
1018 // expand the first number into the first two IDs
1020 while (dataLen
> 0) {
1023 do { /* shift and add in low order 7 bits */
1026 byte
= strm
.ByteDecode();
1027 subId
= (subId
<< 7) + (byte
& 0x7f);
1029 } while ((byte
& 0x80) != 0);
1030 value
.SetAt(i
++, subId
);
1034 * The first two subidentifiers are encoded into the first component
1035 * with the value (X * 40) + Y, where:
1036 * X is the value of the first subidentifier.
1037 * Y is the value of the second subidentifier.
1044 else if (subId
< 80) {
1046 value
[1] = subId
-40;
1050 value
[1] = subId
-80;
1057 void PASN_ObjectId::CommonEncode(PBYTEArray
& encodecObjectId
) const
1059 PINDEX length
= value
.GetSize();
1060 const unsigned * objId
= value
;
1063 // Thise case is really illegal, but we have to do SOMETHING
1064 encodecObjectId
.SetSize(0);
1068 unsigned subId
= (objId
[0] * 40) + objId
[1];
1071 PINDEX outputPosition
= 0;
1073 while (--length
> 0) {
1075 encodecObjectId
[outputPosition
++] = (BYTE
)subId
;
1077 unsigned mask
= 0x7F; /* handle subid == 0 case */
1080 /* testmask *MUST* !!!! be of an unsigned type */
1081 unsigned testmask
= 0x7F;
1083 while (testmask
!= 0) {
1084 if (subId
& testmask
) { /* if any bits set */
1092 /* mask can't be zero here */
1093 while (mask
!= 0x7F) {
1094 /* fix a mask that got truncated above */
1095 if (mask
== 0x1E00000)
1098 encodecObjectId
[outputPosition
++] = (BYTE
)(((subId
& mask
) >> bits
) | 0x80);
1104 encodecObjectId
[outputPosition
++] = (BYTE
)(subId
& mask
);
1113 PINDEX
PASN_ObjectId::GetDataLength() const
1116 CommonEncode(dummy
);
1117 return dummy
.GetSize();
1121 BOOL
PASN_ObjectId::Decode(PASN_Stream
& strm
)
1123 return strm
.ObjectIdDecode(*this);
1127 void PASN_ObjectId::Encode(PASN_Stream
& strm
) const
1129 strm
.ObjectIdEncode(*this);
1133 ///////////////////////////////////////////////////////////////////////
1135 PASN_BitString::PASN_BitString(unsigned nBits
, const BYTE
* buf
)
1136 : PASN_ConstrainedObject(UniversalBitString
, UniversalTagClass
),
1138 bitData((totalBits
+7)/8)
1141 memcpy(bitData
.GetPointer(), buf
, bitData
.GetSize());
1145 PASN_BitString::PASN_BitString(unsigned tag
, TagClass tagClass
, unsigned nBits
)
1146 : PASN_ConstrainedObject(tag
, tagClass
),
1148 bitData((totalBits
+7)/8)
1153 PASN_BitString::PASN_BitString(const PASN_BitString
& other
)
1154 : PASN_ConstrainedObject(other
),
1155 bitData(other
.bitData
, other
.bitData
.GetSize())
1157 totalBits
= other
.totalBits
;
1161 PASN_BitString
& PASN_BitString::operator=(const PASN_BitString
& other
)
1163 PASN_ConstrainedObject::operator=(other
);
1164 totalBits
= other
.totalBits
;
1165 bitData
= PBYTEArray(other
.bitData
, other
.bitData
.GetSize());
1170 void PASN_BitString::SetData(unsigned nBits
, const PBYTEArray
& bytes
)
1172 if ((PINDEX
)nBits
>= MaximumStringSize
)
1180 void PASN_BitString::SetData(unsigned nBits
, const BYTE
* buf
, PINDEX size
)
1182 if ((PINDEX
)nBits
>= MaximumStringSize
)
1187 memcpy(bitData
.GetPointer(size
), buf
, size
);
1192 BOOL
PASN_BitString::SetSize(unsigned nBits
)
1194 if (!CheckByteOffset(nBits
))
1197 if (constraint
== Unconstrained
)
1199 else if (totalBits
< (unsigned)lowerLimit
) {
1202 totalBits
= lowerLimit
;
1203 } else if ((unsigned)totalBits
> upperLimit
) {
1204 if (upperLimit
> (unsigned)MaximumSetSize
)
1206 totalBits
= upperLimit
;
1209 return bitData
.SetSize((totalBits
+7)/8);
1213 bool PASN_BitString::operator[](PINDEX bit
) const
1215 if ((unsigned)bit
< totalBits
)
1216 return (bitData
[bit
>>3] & (1 << (7 - (bit
&7)))) != 0;
1221 void PASN_BitString::Set(unsigned bit
)
1223 if (bit
< totalBits
)
1224 bitData
[(PINDEX
)(bit
>>3)] |= 1 << (7 - (bit
&7));
1228 void PASN_BitString::Clear(unsigned bit
)
1230 if (bit
< totalBits
)
1231 bitData
[(PINDEX
)(bit
>>3)] &= ~(1 << (7 - (bit
&7)));
1235 void PASN_BitString::Invert(unsigned bit
)
1237 if (bit
< totalBits
)
1238 bitData
[(PINDEX
)(bit
>>3)] ^= 1 << (7 - (bit
&7));
1242 PObject::Comparison
PASN_BitString::Compare(const PObject
& obj
) const
1244 PAssert(PIsDescendant(&obj
, PASN_BitString
), PInvalidCast
);
1245 const PASN_BitString
& other
= (const PASN_BitString
&)obj
;
1246 if (totalBits
< other
.totalBits
)
1248 if (totalBits
> other
.totalBits
)
1250 return bitData
.Compare(other
.bitData
);
1254 PObject
* PASN_BitString::Clone() const
1256 PAssert(IsClass(PASN_BitString::Class()), PInvalidCast
);
1257 return new PASN_BitString(*this);
1261 void PASN_BitString::PrintOn(ostream
& strm
) const
1263 int indent
= strm
.precision() + 2;
1264 _Ios_Fmtflags flags
= strm
.flags();
1266 if (totalBits
> 128)
1268 << hex
<< setfill('0') << resetiosflags(ios::floatfield
) << setiosflags(ios::fixed
)
1269 << setw(16) << setprecision(indent
) << bitData
1270 << dec
<< setfill(' ') << resetiosflags(ios::floatfield
)
1271 << setw(indent
-1) << "}";
1272 else if (totalBits
> 32)
1274 << hex
<< setfill('0') << resetiosflags(ios::floatfield
) << setiosflags(ios::fixed
)
1275 << setprecision(2) << setw(16) << bitData
1276 << dec
<< setfill(' ') << resetiosflags(ios::floatfield
);
1280 for (unsigned i
= 0; i
< totalBits
; i
++) {
1281 strm
<< ((bitData
[offset
]&mask
) != 0 ? '1' : '0');
1294 void PASN_BitString::SetConstraintBounds(ConstraintType type
, int lower
, unsigned upper
)
1299 PASN_ConstrainedObject::SetConstraintBounds(type
, lower
, upper
);
1304 PString
PASN_BitString::GetTypeAsString() const
1306 return "Bit String";
1310 PINDEX
PASN_BitString::GetDataLength() const
1312 return (totalBits
+7)/8 + 1;
1316 BOOL
PASN_BitString::Decode(PASN_Stream
& strm
)
1318 return strm
.BitStringDecode(*this);
1322 void PASN_BitString::Encode(PASN_Stream
& strm
) const
1324 strm
.BitStringEncode(*this);
1328 ///////////////////////////////////////////////////////////////////////
1330 PASN_OctetString::PASN_OctetString(const char * str
, PINDEX size
)
1331 : PASN_ConstrainedObject(UniversalOctetString
, UniversalTagClass
)
1335 size
= ::strlen(str
);
1336 SetValue((const BYTE
*)str
, size
);
1341 PASN_OctetString::PASN_OctetString(unsigned tag
, TagClass tagClass
)
1342 : PASN_ConstrainedObject(tag
, tagClass
)
1347 PASN_OctetString::PASN_OctetString(const PASN_OctetString
& other
)
1348 : PASN_ConstrainedObject(other
),
1349 value(other
.value
, other
.GetSize())
1354 PASN_OctetString
& PASN_OctetString::operator=(const PASN_OctetString
& other
)
1356 PASN_ConstrainedObject::operator=(other
);
1357 value
= PBYTEArray(other
.value
, other
.GetSize());
1362 PASN_OctetString
& PASN_OctetString::operator=(const char * str
)
1365 value
.SetSize(lowerLimit
);
1367 SetValue((const BYTE
*)str
, strlen(str
));
1372 PASN_OctetString
& PASN_OctetString::operator=(const PString
& str
)
1374 SetValue((const BYTE
*)(const char *)str
, str
.GetSize()-1);
1379 PASN_OctetString
& PASN_OctetString::operator=(const PBYTEArray
& arr
)
1381 PINDEX len
= arr
.GetSize();
1382 if ((unsigned)len
> upperLimit
|| (int)len
< lowerLimit
)
1390 void PASN_OctetString::SetValue(const BYTE
* data
, PINDEX len
)
1392 if ((unsigned)len
> upperLimit
)
1394 if (SetSize((int)len
< lowerLimit
? lowerLimit
: len
))
1395 memcpy(value
.GetPointer(), data
, len
);
1399 PString
PASN_OctetString::AsString() const
1401 if (value
.IsEmpty())
1403 return PString((const char *)(const BYTE
*)value
, value
.GetSize());
1407 PObject::Comparison
PASN_OctetString::Compare(const PObject
& obj
) const
1409 PAssert(PIsDescendant(&obj
, PASN_OctetString
), PInvalidCast
);
1410 const PASN_OctetString
& other
= (const PASN_OctetString
&)obj
;
1411 return value
.Compare(other
.value
);
1415 PObject
* PASN_OctetString::Clone() const
1417 PAssert(IsClass(PASN_OctetString::Class()), PInvalidCast
);
1418 return new PASN_OctetString(*this);
1422 void PASN_OctetString::PrintOn(ostream
& strm
) const
1424 int indent
= strm
.precision() + 2;
1425 _Ios_Fmtflags flags
= strm
.flags();
1427 strm
<< ' ' << value
.GetSize() << " octets {\n"
1428 << hex
<< setfill('0') << resetiosflags(ios::floatfield
)
1429 << setprecision(indent
) << setw(16);
1431 if (value
.GetSize() <= 32 || (flags
&ios::floatfield
) != ios::fixed
)
1432 strm
<< value
<< '\n';
1434 PBYTEArray
truncatedArray(value
, 32);
1435 strm
<< truncatedArray
<< '\n'
1437 << setw(indent
+4) << "...\n";
1440 strm
<< dec
<< setfill(' ')
1441 << setw(indent
-1) << "}";
1447 void PASN_OctetString::SetConstraintBounds(ConstraintType type
, int lower
, unsigned upper
)
1452 PASN_ConstrainedObject::SetConstraintBounds(type
, lower
, upper
);
1457 PString
PASN_OctetString::GetTypeAsString() const
1459 return "Octet String";
1463 PINDEX
PASN_OctetString::GetDataLength() const
1465 return value
.GetSize();
1469 BOOL
PASN_OctetString::SetSize(PINDEX newSize
)
1471 if (!CheckByteOffset(newSize
, MaximumStringSize
))
1474 if (constraint
!= Unconstrained
) {
1475 if (newSize
< (PINDEX
)lowerLimit
) {
1478 newSize
= lowerLimit
;
1479 } else if ((unsigned)newSize
> upperLimit
) {
1480 if (upperLimit
> (unsigned)MaximumStringSize
)
1482 newSize
= upperLimit
;
1486 return value
.SetSize(newSize
);
1490 BOOL
PASN_OctetString::Decode(PASN_Stream
& strm
)
1492 return strm
.OctetStringDecode(*this);
1496 void PASN_OctetString::Encode(PASN_Stream
& strm
) const
1498 strm
.OctetStringEncode(*this);
1501 ///////////////////////////////////////////////////////////////////////
1503 PASN_ConstrainedString::PASN_ConstrainedString(const char * canonical
, PINDEX size
,
1504 unsigned tag
, TagClass tagClass
)
1505 : PASN_ConstrainedObject(tag
, tagClass
)
1507 canonicalSet
= canonical
;
1508 canonicalSetSize
= size
;
1509 canonicalSetBits
= CountBits(size
);
1510 SetCharacterSet(canonicalSet
, canonicalSetSize
, Unconstrained
);
1514 PASN_ConstrainedString
& PASN_ConstrainedString::operator=(const char * str
)
1519 PStringStream newValue
;
1521 PINDEX len
= strlen(str
);
1523 // Can't copy any more characters than the upper constraint
1524 if ((unsigned)len
> upperLimit
)
1527 // Now copy individual characters, if they are in character set constraint
1528 for (PINDEX i
= 0; i
< len
; i
++) {
1529 PINDEX sz
= characterSet
.GetSize();
1530 if (sz
== 0 || memchr(characterSet
, str
[i
], sz
) != NULL
)
1534 // Make sure string meets minimum length constraint
1535 while ((int)len
< lowerLimit
) {
1536 newValue
<< characterSet
[0];
1541 value
.MakeMinimumSize();
1546 void PASN_ConstrainedString::SetCharacterSet(ConstraintType ctype
, const char * set
)
1548 SetCharacterSet(set
, strlen(set
), ctype
);
1552 void PASN_ConstrainedString::SetCharacterSet(ConstraintType ctype
, unsigned firstChar
, unsigned lastChar
)
1555 for (unsigned i
= firstChar
; i
< lastChar
; i
++)
1556 buffer
[i
] = (char)i
;
1557 SetCharacterSet(buffer
, lastChar
- firstChar
+ 1, ctype
);
1561 void PASN_ConstrainedString::SetCharacterSet(const char * set
, PINDEX setSize
, ConstraintType ctype
)
1563 if (ctype
== Unconstrained
) {
1564 characterSet
.SetSize(canonicalSetSize
);
1565 memcpy(characterSet
.GetPointer(), canonicalSet
, canonicalSetSize
);
1567 else if (setSize
>= MaximumSetSize
||
1568 canonicalSetSize
>= MaximumSetSize
||
1569 characterSet
.GetSize() >= MaximumSetSize
)
1572 characterSet
.SetSize(setSize
);
1574 for (PINDEX i
= 0; i
< canonicalSetSize
; i
++) {
1575 if (memchr(set
, canonicalSet
[i
], setSize
) != NULL
)
1576 characterSet
[count
++] = canonicalSet
[i
];
1580 characterSet
.SetSize(count
);
1583 charSetUnalignedBits
= CountBits(characterSet
.GetSize());
1585 charSetAlignedBits
= 1;
1586 while (charSetUnalignedBits
> charSetAlignedBits
)
1587 charSetAlignedBits
<<= 1;
1589 operator=((const char *)value
);
1593 PObject::Comparison
PASN_ConstrainedString::Compare(const PObject
& obj
) const
1595 PAssert(PIsDescendant(&obj
, PASN_ConstrainedString
), PInvalidCast
);
1596 const PASN_ConstrainedString
& other
= (const PASN_ConstrainedString
&)obj
;
1597 return value
.Compare(other
.value
);
1601 void PASN_ConstrainedString::PrintOn(ostream
& strm
) const
1603 strm
<< value
.ToLiteral();
1607 void PASN_ConstrainedString::SetConstraintBounds(ConstraintType type
,
1608 int lower
, unsigned upper
)
1613 PASN_ConstrainedObject::SetConstraintBounds(type
, lower
, upper
);
1614 if (constraint
!= Unconstrained
) {
1615 if (value
.GetSize() < (PINDEX
)lowerLimit
)
1616 value
.SetSize(lowerLimit
);
1617 else if ((unsigned)value
.GetSize() > upperLimit
)
1618 value
.SetSize(upperLimit
);
1623 PINDEX
PASN_ConstrainedString::GetDataLength() const
1625 return value
.GetSize()-1;
1629 BOOL
PASN_ConstrainedString::Decode(PASN_Stream
& strm
)
1631 return strm
.ConstrainedStringDecode(*this);
1635 void PASN_ConstrainedString::Encode(PASN_Stream
& strm
) const
1637 strm
.ConstrainedStringEncode(*this);
1641 #define DEFINE_STRING_CLASS(name, set) \
1642 static const char name##StringSet[] = set; \
1643 PASN_##name##String::PASN_##name##String(const char * str) \
1644 : PASN_ConstrainedString(name##StringSet, sizeof(name##StringSet)-1, \
1645 Universal##name##String, UniversalTagClass) \
1646 { PASN_ConstrainedString::SetValue(str); } \
1647 PASN_##name##String::PASN_##name##String(unsigned tag, TagClass tagClass) \
1648 : PASN_ConstrainedString(name##StringSet, sizeof(name##StringSet)-1, tag, tagClass) \
1650 PASN_##name##String & PASN_##name##String::operator=(const char * str) \
1651 { PASN_ConstrainedString::SetValue(str); return *this; } \
1652 PASN_##name##String & PASN_##name##String::operator=(const PString & str) \
1653 { PASN_ConstrainedString::SetValue(str); return *this; } \
1654 PObject * PASN_##name##String::Clone() const \
1655 { PAssert(IsClass(PASN_##name##String::Class()), PInvalidCast); \
1656 return new PASN_##name##String(*this); } \
1657 PString PASN_##name##String::GetTypeAsString() const \
1658 { return #name " String"; }
1660 DEFINE_STRING_CLASS(Numeric
, " 0123456789")
1661 DEFINE_STRING_CLASS(Printable
, " '()+,-./0123456789:=?"
1662 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1663 "abcdefghijklmnopqrstuvwxyz")
1664 DEFINE_STRING_CLASS(Visible
, " !\"#$%&'()*+,-./0123456789:;<=>?"
1665 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
1666 "`abcdefghijklmnopqrstuvwxyz{|}~")
1667 DEFINE_STRING_CLASS(IA5
, "\000\001\002\003\004\005\006\007"
1668 "\010\011\012\013\014\015\016\017"
1669 "\020\021\022\023\024\025\026\027"
1670 "\030\031\032\033\034\035\036\037"
1671 " !\"#$%&'()*+,-./0123456789:;<=>?"
1672 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
1673 "`abcdefghijklmnopqrstuvwxyz{|}~\177")
1674 DEFINE_STRING_CLASS(General
, "\000\001\002\003\004\005\006\007"
1675 "\010\011\012\013\014\015\016\017"
1676 "\020\021\022\023\024\025\026\027"
1677 "\030\031\032\033\034\035\036\037"
1678 " !\"#$%&'()*+,-./0123456789:;<=>?"
1679 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
1680 "`abcdefghijklmnopqrstuvwxyz{|}~\177"
1681 "\200\201\202\203\204\205\206\207"
1682 "\210\211\212\213\214\215\216\217"
1683 "\220\221\222\223\224\225\226\227"
1684 "\230\231\232\233\234\235\236\237"
1685 "\240\241\242\243\244\245\246\247"
1686 "\250\251\252\253\254\255\256\257"
1687 "\260\261\262\263\264\265\266\267"
1688 "\270\271\272\273\274\275\276\277"
1689 "\300\301\302\303\304\305\306\307"
1690 "\310\311\312\313\314\315\316\317"
1691 "\320\321\322\323\324\325\326\327"
1692 "\330\331\332\333\334\335\336\337"
1693 "\340\341\342\343\344\345\346\347"
1694 "\350\351\352\353\354\355\356\357"
1695 "\360\361\362\363\364\365\366\367"
1696 "\370\371\372\373\374\375\376\377")
1699 ///////////////////////////////////////////////////////////////////////
1701 PASN_BMPString::PASN_BMPString(const char * str
)
1702 : PASN_ConstrainedObject(UniversalBMPString
, UniversalTagClass
)
1710 PASN_BMPString::PASN_BMPString(const PWORDArray
& wstr
)
1711 : PASN_ConstrainedObject(UniversalBMPString
, UniversalTagClass
)
1718 PASN_BMPString::PASN_BMPString(unsigned tag
, TagClass tagClass
)
1719 : PASN_ConstrainedObject(tag
, tagClass
)
1725 void PASN_BMPString::Construct()
1729 charSetAlignedBits
= 16;
1730 charSetUnalignedBits
= 16;
1734 PASN_BMPString::PASN_BMPString(const PASN_BMPString
& other
)
1735 : PASN_ConstrainedObject(other
),
1736 value(other
.value
, other
.value
.GetSize()),
1737 characterSet(other
.characterSet
)
1739 firstChar
= other
.firstChar
;
1740 lastChar
= other
.lastChar
;
1741 charSetAlignedBits
= other
.charSetAlignedBits
;
1742 charSetUnalignedBits
= other
.charSetUnalignedBits
;
1746 PASN_BMPString
& PASN_BMPString::operator=(const PASN_BMPString
& other
)
1748 PASN_ConstrainedObject::operator=(other
);
1750 value
= PWORDArray(other
.value
, other
.value
.GetSize());
1751 characterSet
= other
.characterSet
;
1752 firstChar
= other
.firstChar
;
1753 lastChar
= other
.lastChar
;
1754 charSetAlignedBits
= other
.charSetAlignedBits
;
1755 charSetUnalignedBits
= other
.charSetUnalignedBits
;
1761 BOOL
PASN_BMPString::IsLegalCharacter(WORD ch
)
1769 if (characterSet
.IsEmpty())
1772 const WORD
* wptr
= characterSet
;
1773 PINDEX count
= characterSet
.GetSize();
1774 while (count
-- > 0) {
1784 PASN_BMPString
& PASN_BMPString::operator=(const PWORDArray
& array
)
1786 PINDEX paramSize
= array
.GetSize();
1788 // Can't copy any more than the upper constraint
1789 if ((unsigned)paramSize
> upperLimit
)
1790 paramSize
= upperLimit
;
1792 // Number of bytes must be at least lhe lower constraint
1793 PINDEX newSize
= (int)paramSize
< lowerLimit
? lowerLimit
: paramSize
;
1794 value
.SetSize(newSize
);
1797 for (PINDEX i
= 0; i
< paramSize
; i
++) {
1799 if (IsLegalCharacter(c
))
1803 // Pad out with the first character till required size
1804 while (count
< newSize
)
1805 value
[count
++] = firstChar
;
1811 void PASN_BMPString::SetCharacterSet(ConstraintType ctype
, const char * charSet
)
1813 PWORDArray
array(strlen(charSet
));
1816 while (*charSet
!= '\0')
1817 array
[count
++] = (BYTE
)*charSet
++;
1819 SetCharacterSet(ctype
, array
);
1823 void PASN_BMPString::SetCharacterSet(ConstraintType ctype
, const PWORDArray
& charSet
)
1825 if (ctype
== Unconstrained
) {
1828 characterSet
.SetSize(0);
1831 characterSet
= charSet
;
1833 charSetUnalignedBits
= CountBits(lastChar
- firstChar
+ 1);
1834 if (!charSet
.IsEmpty()) {
1836 for (PINDEX i
= 0; i
< charSet
.GetSize(); i
++) {
1837 if (characterSet
[i
] >= firstChar
&& characterSet
[i
] <= lastChar
)
1840 count
= CountBits(count
);
1841 if (charSetUnalignedBits
> count
)
1842 charSetUnalignedBits
= count
;
1845 charSetAlignedBits
= 1;
1846 while (charSetUnalignedBits
> charSetAlignedBits
)
1847 charSetAlignedBits
<<= 1;
1854 void PASN_BMPString::SetCharacterSet(ConstraintType ctype
, unsigned first
, unsigned last
)
1856 if (ctype
!= Unconstrained
) {
1857 PAssert(first
< 0x10000 && last
< 0x10000 && last
> first
, PInvalidParameter
);
1858 firstChar
= (WORD
)first
;
1859 lastChar
= (WORD
)last
;
1861 SetCharacterSet(ctype
, characterSet
);
1865 PObject
* PASN_BMPString::Clone() const
1867 PAssert(IsClass(PASN_BMPString::Class()), PInvalidCast
);
1868 return new PASN_BMPString(*this);
1872 PObject::Comparison
PASN_BMPString::Compare(const PObject
& obj
) const
1874 PAssert(PIsDescendant(&obj
, PASN_BMPString
), PInvalidCast
);
1875 const PASN_BMPString
& other
= (const PASN_BMPString
&)obj
;
1876 return value
.Compare(other
.value
);
1880 void PASN_BMPString::PrintOn(ostream
& strm
) const
1882 int indent
= strm
.precision() + 2;
1883 PINDEX sz
= value
.GetSize();
1884 strm
<< ' ' << sz
<< " characters {\n";
1887 strm
<< setw(indent
) << " " << hex
<< setfill('0');
1889 for (j
= 0; j
< 8; j
++)
1891 strm
<< setw(4) << value
[i
+j
] << ' ';
1895 for (j
= 0; j
< 8; j
++) {
1897 WORD c
= value
[i
+j
];
1898 if (c
< 128 && isprint(c
))
1904 strm
<< dec
<< setfill(' ') << '\n';
1907 strm
<< setw(indent
-1) << "}";
1911 PString
PASN_BMPString::GetTypeAsString() const
1913 return "BMP String";
1917 PINDEX
PASN_BMPString::GetDataLength() const
1919 return value
.GetSize()*2;
1923 BOOL
PASN_BMPString::Decode(PASN_Stream
& strm
)
1925 return strm
.BMPStringDecode(*this);
1929 void PASN_BMPString::Encode(PASN_Stream
& strm
) const
1931 strm
.BMPStringEncode(*this);
1935 ///////////////////////////////////////////////////////////////////////
1937 PASN_GeneralisedTime
& PASN_GeneralisedTime::operator=(const PTime
& time
)
1939 value
= time
.AsString("yyyyMMddhhmmss.uz");
1940 value
.Replace("GMT", "Z");
1945 PTime
PASN_GeneralisedTime::GetValue() const
1947 int year
= value(0,3).AsInteger();
1948 int month
= value(4,5).AsInteger();
1949 int day
= value(6,7).AsInteger();
1950 int hour
= value(8,9).AsInteger();
1951 int minute
= value(10,11).AsInteger();
1955 if (isdigit(value
[12])) {
1956 seconds
= value(12,13).AsInteger();
1957 if (value
[14] != '.')
1961 while (isdigit(value
[zonePos
]))
1966 int zone
= PTime::Local
;
1967 switch (value
[zonePos
]) {
1973 zone
= value(zonePos
+1,zonePos
+2).AsInteger()*60 +
1974 value(zonePos
+3,zonePos
+4).AsInteger();
1977 return PTime(seconds
, minute
, hour
, day
, month
, year
, zone
);
1981 ///////////////////////////////////////////////////////////////////////
1983 PASN_UniversalTime
& PASN_UniversalTime::operator=(const PTime
& time
)
1985 value
= time
.AsString("yyMMddhhmmssz");
1986 value
.Replace("GMT", "Z");
1987 value
.MakeMinimumSize();
1992 PTime
PASN_UniversalTime::GetValue() const
1994 int year
= value(0,1).AsInteger();
2000 int month
= value(2,3).AsInteger();
2001 int day
= value(4,5).AsInteger();
2002 int hour
= value(6,7).AsInteger();
2003 int minute
= value(8,9).AsInteger();
2007 if (isdigit(value
[10])) {
2008 seconds
= value(10,11).AsInteger();
2012 int zone
= PTime::UTC
;
2013 if (value
[zonePos
] != 'Z')
2014 zone
= value(zonePos
+1,zonePos
+2).AsInteger()*60 +
2015 value(zonePos
+3,zonePos
+4).AsInteger();
2017 return PTime(seconds
, minute
, hour
, day
, month
, year
, zone
);
2021 ///////////////////////////////////////////////////////////////////////
2023 PASN_Choice::PASN_Choice(unsigned nChoices
, BOOL extend
)
2024 : PASN_Object(0, ApplicationTagClass
, extend
)
2026 numChoices
= nChoices
;
2031 PASN_Choice::PASN_Choice(unsigned tag
, TagClass tagClass
,
2032 unsigned upper
, BOOL extend
)
2033 : PASN_Object(tag
, tagClass
, extend
)
2040 PASN_Choice::PASN_Choice(unsigned tag
, TagClass tagClass
,
2041 unsigned upper
, BOOL extend
, const PString
& nameSpec
)
2042 : PASN_Object(tag
, tagClass
, extend
),
2043 names(BuildNamesDict(nameSpec
))
2050 PASN_Choice::PASN_Choice(const PASN_Choice
& other
)
2051 : PASN_Object(other
),
2054 numChoices
= other
.numChoices
;
2056 if (other
.CheckCreate())
2057 choice
= (PASN_Object
*)other
.choice
->Clone();
2063 PASN_Choice
& PASN_Choice::operator=(const PASN_Choice
& other
)
2065 if (&other
== this) // Assigning to ourself, just do nothing.
2070 PASN_Object::operator=(other
);
2072 numChoices
= other
.numChoices
;
2073 names
= other
.names
;
2075 if (other
.CheckCreate())
2076 choice
= (PASN_Object
*)other
.choice
->Clone();
2084 PASN_Choice::~PASN_Choice()
2090 void PASN_Choice::SetTag(unsigned newTag
, TagClass tagClass
)
2092 PASN_Object::SetTag(newTag
, tagClass
);
2097 choice
->SetTag(newTag
, tagClass
);
2101 PString
PASN_Choice::GetTagName() const
2103 if (names
.Contains(tag
))
2106 if (CheckCreate() &&
2107 PIsDescendant(choice
, PASN_Choice
) &&
2108 choice
->GetTag() == tag
&&
2109 choice
->GetTagClass() == tagClass
)
2110 return PString(choice
->GetClass()) + "->" + ((PASN_Choice
*)choice
)->GetTagName();
2112 return psprintf("<%u>", tag
);
2116 BOOL
PASN_Choice::CheckCreate() const
2121 return ((PASN_Choice
*)this)->CreateObject();
2125 PASN_Object
& PASN_Choice::GetObject() const
2127 PAssert(CheckCreate(), "NULL Choice");
2132 #if defined(__GNUC__) && __GNUC__ <= 2 && __GNUC_MINOR__ < 9
2134 #define CHOICE_CAST_OPERATOR(cls) \
2135 PASN_Choice::operator cls &() const \
2137 PAssert(CheckCreate(), "Cast of NULL choice"); \
2138 PAssert(choice->IsDescendant(cls::Class()), PInvalidCast); \
2139 return *(cls *)choice; \
2144 #define CHOICE_CAST_OPERATOR(cls) \
2145 PASN_Choice::operator cls &() \
2147 PAssert(CheckCreate(), "Cast of NULL choice"); \
2148 PAssert(PIsDescendant(choice, cls), PInvalidCast); \
2149 return *(cls *)choice; \
2151 PASN_Choice::operator const cls &() const \
2153 PAssert(CheckCreate(), "Cast of NULL choice"); \
2154 PAssert(PIsDescendant(choice, cls), PInvalidCast); \
2155 return *(const cls *)choice; \
2161 CHOICE_CAST_OPERATOR(PASN_Null
)
2162 CHOICE_CAST_OPERATOR(PASN_Boolean
)
2163 CHOICE_CAST_OPERATOR(PASN_Integer
)
2164 CHOICE_CAST_OPERATOR(PASN_Enumeration
)
2165 CHOICE_CAST_OPERATOR(PASN_Real
)
2166 CHOICE_CAST_OPERATOR(PASN_ObjectId
)
2167 CHOICE_CAST_OPERATOR(PASN_BitString
)
2168 CHOICE_CAST_OPERATOR(PASN_OctetString
)
2169 CHOICE_CAST_OPERATOR(PASN_NumericString
)
2170 CHOICE_CAST_OPERATOR(PASN_PrintableString
)
2171 CHOICE_CAST_OPERATOR(PASN_VisibleString
)
2172 CHOICE_CAST_OPERATOR(PASN_IA5String
)
2173 CHOICE_CAST_OPERATOR(PASN_GeneralString
)
2174 CHOICE_CAST_OPERATOR(PASN_BMPString
)
2175 CHOICE_CAST_OPERATOR(PASN_Sequence
)
2178 PObject::Comparison
PASN_Choice::Compare(const PObject
& obj
) const
2180 PAssert(PIsDescendant(&obj
, PASN_Choice
), PInvalidCast
);
2181 const PASN_Choice
& other
= (const PASN_Choice
&)obj
;
2184 other
.CheckCreate();
2186 if (choice
== other
.choice
)
2192 if (other
.choice
== NULL
)
2195 if (tag
< other
.tag
)
2198 if (tag
> other
.tag
)
2201 return choice
->Compare(*other
.choice
);
2205 void PASN_Choice::PrintOn(ostream
& strm
) const
2207 strm
<< GetTagName();
2210 strm
<< ' ' << *choice
;
2216 PString
PASN_Choice::GetTypeAsString() const
2222 PINDEX
PASN_Choice::GetDataLength() const
2225 return choice
->GetDataLength();
2231 BOOL
PASN_Choice::IsPrimitive() const
2234 return choice
->IsPrimitive();
2239 BOOL
PASN_Choice::Decode(PASN_Stream
& strm
)
2241 return strm
.ChoiceDecode(*this);
2245 void PASN_Choice::Encode(PASN_Stream
& strm
) const
2247 strm
.ChoiceEncode(*this);
2251 ///////////////////////////////////////////////////////////////////////
2253 PASN_Sequence::PASN_Sequence(unsigned tag
, TagClass tagClass
,
2254 unsigned nOpts
, BOOL extend
, unsigned nExtend
)
2255 : PASN_Object(tag
, tagClass
, extend
)
2257 optionMap
.SetConstraints(PASN_ConstrainedObject::FixedConstraint
, nOpts
);
2258 knownExtensions
= nExtend
;
2259 totalExtensions
= 0;
2260 endBasicEncoding
= 0;
2264 PASN_Sequence::PASN_Sequence(const PASN_Sequence
& other
)
2265 : PASN_Object(other
),
2266 fields(other
.fields
.GetSize()),
2267 optionMap(other
.optionMap
),
2268 extensionMap(other
.extensionMap
)
2270 for (PINDEX i
= 0; i
< other
.fields
.GetSize(); i
++)
2271 fields
.SetAt(i
, other
.fields
[i
].Clone());
2273 knownExtensions
= other
.knownExtensions
;
2274 totalExtensions
= other
.totalExtensions
;
2275 endBasicEncoding
= 0;
2279 PASN_Sequence
& PASN_Sequence::operator=(const PASN_Sequence
& other
)
2281 PASN_Object::operator=(other
);
2283 fields
.SetSize(other
.fields
.GetSize());
2284 for (PINDEX i
= 0; i
< other
.fields
.GetSize(); i
++)
2285 fields
.SetAt(i
, other
.fields
[i
].Clone());
2287 optionMap
= other
.optionMap
;
2288 knownExtensions
= other
.knownExtensions
;
2289 totalExtensions
= other
.totalExtensions
;
2290 extensionMap
= other
.extensionMap
;
2296 BOOL
PASN_Sequence::HasOptionalField(PINDEX opt
) const
2298 if (opt
< (PINDEX
)optionMap
.GetSize())
2299 return optionMap
[opt
];
2301 return extensionMap
[opt
- optionMap
.GetSize()];
2305 void PASN_Sequence::IncludeOptionalField(PINDEX opt
)
2307 if (opt
< (PINDEX
)optionMap
.GetSize())
2310 PAssert(extendable
, "Must be extendable type");
2311 opt
-= optionMap
.GetSize();
2312 if (opt
>= (PINDEX
)extensionMap
.GetSize())
2313 extensionMap
.SetSize(opt
+1);
2314 extensionMap
.Set(opt
);
2319 void PASN_Sequence::RemoveOptionalField(PINDEX opt
)
2321 if (opt
< (PINDEX
)optionMap
.GetSize())
2322 optionMap
.Clear(opt
);
2324 PAssert(extendable
, "Must be extendable type");
2325 opt
-= optionMap
.GetSize();
2326 extensionMap
.Clear(opt
);
2331 PObject::Comparison
PASN_Sequence::Compare(const PObject
& obj
) const
2333 PAssert(PIsDescendant(&obj
, PASN_Sequence
), PInvalidCast
);
2334 const PASN_Sequence
& other
= (const PASN_Sequence
&)obj
;
2335 return fields
.Compare(other
.fields
);
2339 PObject
* PASN_Sequence::Clone() const
2341 PAssert(IsClass(PASN_Sequence::Class()), PInvalidCast
);
2342 return new PASN_Sequence(*this);
2346 void PASN_Sequence::PrintOn(ostream
& strm
) const
2348 int indent
= strm
.precision() + 2;
2350 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++) {
2351 strm
<< setw(indent
+6) << "field[" << i
<< "] <";
2352 switch (fields
[i
].GetTagClass()) {
2353 case UniversalTagClass
:
2354 strm
<< "Universal";
2356 case ApplicationTagClass
:
2357 strm
<< "Application";
2359 case ContextSpecificTagClass
:
2360 strm
<< "ContextSpecific";
2362 case PrivateTagClass
:
2367 strm
<< '-' << fields
[i
].GetTag() << '-'
2368 << fields
[i
].GetTypeAsString() << "> = "
2369 << fields
[i
] << '\n';
2371 strm
<< setw(indent
-1) << "}";
2375 PString
PASN_Sequence::GetTypeAsString() const
2381 PINDEX
PASN_Sequence::GetDataLength() const
2384 for (PINDEX i
= 0; i
< fields
.GetSize(); i
++)
2385 len
+= fields
[i
].GetObjectLength();
2390 BOOL
PASN_Sequence::IsPrimitive() const
2396 BOOL
PASN_Sequence::Decode(PASN_Stream
& strm
)
2398 return PreambleDecode(strm
) && UnknownExtensionsDecode(strm
);
2402 void PASN_Sequence::Encode(PASN_Stream
& strm
) const
2404 PreambleEncode(strm
);
2405 UnknownExtensionsEncode(strm
);
2409 BOOL
PASN_Sequence::PreambleDecode(PASN_Stream
& strm
)
2411 return strm
.SequencePreambleDecode(*this);
2415 void PASN_Sequence::PreambleEncode(PASN_Stream
& strm
) const
2417 strm
.SequencePreambleEncode(*this);
2421 BOOL
PASN_Sequence::KnownExtensionDecode(PASN_Stream
& strm
, PINDEX fld
, PASN_Object
& field
)
2423 return strm
.SequenceKnownDecode(*this, fld
, field
);
2427 void PASN_Sequence::KnownExtensionEncode(PASN_Stream
& strm
, PINDEX fld
, const PASN_Object
& field
) const
2429 strm
.SequenceKnownEncode(*this, fld
, field
);
2433 BOOL
PASN_Sequence::UnknownExtensionsDecode(PASN_Stream
& strm
)
2435 return strm
.SequenceUnknownDecode(*this);
2439 void PASN_Sequence::UnknownExtensionsEncode(PASN_Stream
& strm
) const
2441 strm
.SequenceUnknownEncode(*this);
2445 ///////////////////////////////////////////////////////////////////////
2447 PASN_Set::PASN_Set(unsigned tag
, TagClass tagClass
,
2448 unsigned nOpts
, BOOL extend
, unsigned nExtend
)
2449 : PASN_Sequence(tag
, tagClass
, nOpts
, extend
, nExtend
)
2454 PObject
* PASN_Set::Clone() const
2456 PAssert(IsClass(PASN_Set::Class()), PInvalidCast
);
2457 return new PASN_Set(*this);
2461 PString
PASN_Set::GetTypeAsString() const
2466 ///////////////////////////////////////////////////////////////////////
2468 PASN_Array::PASN_Array(unsigned tag
, TagClass tagClass
)
2469 : PASN_ConstrainedObject(tag
, tagClass
)
2474 PASN_Array::PASN_Array(const PASN_Array
& other
)
2475 : PASN_ConstrainedObject(other
),
2476 array(other
.array
.GetSize())
2478 for (PINDEX i
= 0; i
< other
.array
.GetSize(); i
++)
2479 array
.SetAt(i
, other
.array
[i
].Clone());
2483 PASN_Array
& PASN_Array::operator=(const PASN_Array
& other
)
2485 PASN_ConstrainedObject::operator=(other
);
2487 array
.SetSize(other
.array
.GetSize());
2488 for (PINDEX i
= 0; i
< other
.array
.GetSize(); i
++)
2489 array
.SetAt(i
, other
.array
[i
].Clone());
2495 BOOL
PASN_Array::SetSize(PINDEX newSize
)
2497 if (newSize
> MaximumArraySize
)
2500 PINDEX originalSize
= array
.GetSize();
2501 if (!array
.SetSize(newSize
))
2504 for (PINDEX i
= originalSize
; i
< newSize
; i
++) {
2505 PASN_Object
* obj
= CreateObject();
2509 array
.SetAt(i
, obj
);
2516 PObject::Comparison
PASN_Array::Compare(const PObject
& obj
) const
2518 PAssert(PIsDescendant(&obj
, PASN_Array
), PInvalidCast
);
2519 const PASN_Array
& other
= (const PASN_Array
&)obj
;
2520 return array
.Compare(other
.array
);
2524 void PASN_Array::PrintOn(ostream
& strm
) const
2526 int indent
= strm
.precision() + 2;
2527 strm
<< array
.GetSize() << " entries {\n";
2528 for (PINDEX i
= 0; i
< array
.GetSize(); i
++)
2529 strm
<< setw(indent
+1) << "[" << i
<< "]=" << setprecision(indent
) << array
[i
] << '\n';
2530 strm
<< setw(indent
-1) << "}";
2534 void PASN_Array::SetConstraintBounds(ConstraintType type
, int lower
, unsigned upper
)
2539 PASN_ConstrainedObject::SetConstraintBounds(type
, lower
, upper
);
2540 if (constraint
!= Unconstrained
) {
2541 if (GetSize() < (PINDEX
)lowerLimit
)
2542 SetSize(lowerLimit
);
2543 else if (GetSize() > (PINDEX
)upperLimit
)
2544 SetSize(upperLimit
);
2549 PString
PASN_Array::GetTypeAsString() const
2555 PINDEX
PASN_Array::GetDataLength() const
2558 for (PINDEX i
= 0; i
< array
.GetSize(); i
++)
2559 len
+= array
[i
].GetObjectLength();
2564 BOOL
PASN_Array::IsPrimitive() const
2570 BOOL
PASN_Array::Decode(PASN_Stream
& strm
)
2572 return strm
.ArrayDecode(*this);
2576 void PASN_Array::Encode(PASN_Stream
& strm
) const
2578 strm
.ArrayEncode(*this);
2582 ///////////////////////////////////////////////////////////////////////
2584 PASN_Stream::PASN_Stream()
2590 PASN_Stream::PASN_Stream(const PBYTEArray
& bytes
)
2597 PASN_Stream::PASN_Stream(const BYTE
* buf
, PINDEX size
)
2598 : PBYTEArray(buf
, size
)
2604 void PASN_Stream::Construct()
2611 void PASN_Stream::PrintOn(ostream
& strm
) const
2613 int indent
= strm
.precision() + 2;
2614 strm
<< " size=" << GetSize()
2615 << " pos=" << byteOffset
<< '.' << (8-bitOffset
)
2618 while (i
< GetSize()) {
2619 strm
<< setw(indent
) << " " << hex
<< setfill('0');
2621 for (j
= 0; j
< 16; j
++)
2622 if (i
+j
< GetSize())
2623 strm
<< setw(2) << (unsigned)(BYTE
)theArray
[i
+j
] << ' ';
2627 for (j
= 0; j
< 16; j
++) {
2628 if (i
+j
< GetSize()) {
2629 BYTE c
= theArray
[i
+j
];
2630 if (c
< 128 && isprint(c
))
2636 strm
<< dec
<< setfill(' ') << '\n';
2639 strm
<< setw(indent
-1) << "}";
2643 void PASN_Stream::SetPosition(PINDEX newPos
)
2645 if (!CheckByteOffset(byteOffset
))
2648 if (newPos
> GetSize())
2649 byteOffset
= GetSize();
2651 byteOffset
= newPos
;
2656 void PASN_Stream::ResetDecoder()
2663 void PASN_Stream::BeginEncoding()
2667 PBYTEArray::operator=(PBYTEArray(20));
2671 void PASN_Stream::CompleteEncoding()
2673 if (byteOffset
!= P_MAX_INDEX
) {
2674 if (bitOffset
!= 8) {
2678 SetSize(byteOffset
);
2679 byteOffset
= P_MAX_INDEX
;
2684 BYTE
PASN_Stream::ByteDecode()
2686 if (!CheckByteOffset(byteOffset
, GetSize()))
2690 return theArray
[byteOffset
++];
2694 void PASN_Stream::ByteEncode(unsigned value
)
2696 if (!CheckByteOffset(byteOffset
))
2699 if (bitOffset
!= 8) {
2703 if (byteOffset
>= GetSize())
2704 SetSize(byteOffset
+10);
2705 theArray
[byteOffset
++] = (BYTE
)value
;
2709 unsigned PASN_Stream::BlockDecode(BYTE
* bufptr
, unsigned nBytes
)
2711 if (nBytes
== 0 || bufptr
== NULL
|| !CheckByteOffset(byteOffset
+nBytes
))
2716 if (byteOffset
+nBytes
> (unsigned)GetSize()) {
2717 nBytes
= GetSize() - byteOffset
;
2722 memcpy(bufptr
, &theArray
[byteOffset
], nBytes
);
2723 byteOffset
+= nBytes
;
2728 void PASN_Stream::BlockEncode(const BYTE
* bufptr
, PINDEX nBytes
)
2730 if (!CheckByteOffset(byteOffset
, GetSize()))
2738 if (byteOffset
+nBytes
>= GetSize())
2739 SetSize(byteOffset
+nBytes
+10);
2741 memcpy(theArray
+byteOffset
, bufptr
, nBytes
);
2742 byteOffset
+= nBytes
;
2746 void PASN_Stream::ByteAlign()
2748 if (!CheckByteOffset(byteOffset
, GetSize()))
2751 if (bitOffset
!= 8) {
2758 ///////////////////////////////////////////////////////////////////////
2760 #ifdef P_INCLUDE_PER
2761 #include "asnper.cxx"
2764 #ifdef P_INCLUDE_BER
2765 #include "asnber.cxx"
2768 #ifdef P_INCLUDE_XER
2769 #include "asnxer.cxx"
2772 // End of file ////////////////////////////////////////////////////////////////