Sign in Join
Wiki copytemporary

XML Decoders: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: XML Decoders

OldNewLine
1+## XML Decoders
2+
3+Starting from SVN revision 12582, XML reader is able to process data in any encoding by using external decoders. Following is the brief description of how it works.
4+
5+### Available decoders
6+
7+Currently, the decoder using libiconv is available. It has two distinct implementations. The first one, in **xmliconv.pas** unit, uses the existing iconvenc package and supports Linux, FreeBSD and Darwin targets. The second one, in **xmliconv_windows.pas** unit, is for Windows targets. It links to the native-build iconv.dll that you should distribute with the application.
8+
9+### Decoder structure
10+
11+Interfacing with the external decoders is done in a plain procedural style. Writing the decoder is essentially implementing the following three procedures:
12+
13+1. `GetDecoder`
14+2. `Decode`
15+3. `Cleanup` (optional)
16+
17+Here is the brief desription of external decoder operation:
18+
19+#### GetDecoder
20+
21+``` pascal
22+function GetDecoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
23+```
24+
25+At the program initialization time, decoder registers itself by calling `XMLRead.RegisterDecoder` procedure, supplying its `GetDecoder` function as the argument. Whenever the reader encounters the encoding label which it does not handle internally, it calls all registered `GetDecoder` functions in the same order they were registered, until one of them returns *True*. The `GetDecoder` function arguments are the name of encoding and the `TDecoder` record that the function should fill. The encoding name is restricted to characters in range \['A'..'Z', 'a'..'z', '0'..'9', '.', '-', '_'\], and must be compared case-insensitive. If the decoder supports given encoding, the function should set at least the `Decode` member of the supplied record and return *True*. Setting other members of `Decoder` is optional.
26+
27+#### Cleanup
28+
29+``` pascal
30+procedure Cleanup(Context: Pointer); stdcall;
31+```
32+
33+If `GetDecoder` sets the `Decoder.Cleanup` member, it is called by reader once, after processing of the current entity is finished. As the name suggests, the decoder should then free all resources it allocated.
34+
35+The value of `Decoder.Context` is passed to `Decode` and `Cleanup` procedures each time they are called. The reader does not assign any meaning to this value.
36+
37+#### Decode
38+
39+``` pascal
40+function Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal;
41+ OutBuf: PWideChar; var OutCnt: Cardinal): Integer; stdcall;
42+```
43+
44+The `Decode` function does the main job. It should convert the input data pointed by `InBuf` into UTF-16 in the current platform endianness and place it into `OutBuf`. The size of input buffer is supplied in `InCnt`, the space avaliable in output buffer is in `OutCnt`.
45+
46+The important difference to note is that `InCnt` is given in **bytes**, while `OutCnt` is in **WideChars**.
47+
48+The function must decrement `InCnt` and `OutCnt` according to the amount of data it processes. Each processed character decrements `OutCnt` by one (or by two in case the surrogate pair is written); the amount of `InCnt` decrement depends on the actual encoding.
49+
50+No assumptions should be made about initial size of buffers: for example, the reader may call decoder with only a few bytes in input buffer. The decoder function then should return zero indicating nothing is processed, and the reader will fetch more input and call decoder again.
51+
52+The function should return positive value if it had processed something, zero if it had not (e.g. because no space available in either input or output buffer), and negative value in cause the input data contains illegal sequence. In the future, there may be attempt to categorize the decoding errors, but currently any negative return simply aborts the reader with the 'Decoding error' message.
53+
54+In case of error in input data the decoder should still decrement `OutCnt` to reflect the number of successfully processed characters. This will be used by reader to provide location information in the exception error message.
55+
56+### Sample decoder
57+
58+Following is a sample unit that decodes cp866. This decoder is stateless, so it does not use the `Cleanup` and `Context` members. It should be very easy to modify this sample to handle any similar single-byte encoding by just replacing the conversion table.
59+
60+``` pascal
61+unit xmlcp866;
62+
63+interface
64+
65+implementation
66+
67+uses
68+ SysUtils, xmlread;
69+
70+const
71+ cp866table: array[#128..#255] of WideChar=(
72+ #$0410, #$0411, #$0412, #$0413, #$0414, #$0415, #$0416, #$0417,
73+ #$0418, #$0419, #$041A, #$041B, #$041C, #$041D, #$041E, #$041F,
74+ #$0420, #$0421, #$0422, #$0423, #$0424, #$0425, #$0426, #$0427,
75+ #$0428, #$0429, #$042A, #$042B, #$042C, #$042D, #$042E, #$042F,
76+ #$0430, #$0431, #$0432, #$0433, #$0434, #$0435, #$0436, #$0437,
77+ #$0438, #$0439, #$043A, #$043B, #$043C, #$043D, #$043E, #$043F,
78+ #$2591, #$2592, #$2593, #$2502, #$2524, #$2561, #$2562, #$2556,
79+ #$2555, #$2563, #$2551, #$2557, #$255D, #$255C, #$255B, #$2510,
80+ #$2514, #$2534, #$252C, #$251C, #$2500, #$253C, #$255E, #$255F,
81+ #$255A, #$2554, #$2569, #$2566, #$2560, #$2550, #$256C, #$2567,
82+ #$2568, #$2564, #$2565, #$2559, #$2558, #$2552, #$2553, #$256B,
83+ #$256A, #$2518, #$250C, #$2588, #$2584, #$258C, #$2590, #$2580,
84+ #$0440, #$0441, #$0442, #$0443, #$0444, #$0445, #$0446, #$0447,
85+ #$0448, #$0449, #$044A, #$044B, #$044C, #$044D, #$044E, #$044F,
86+ #$0401, #$0451, #$0404, #$0454, #$0407, #$0457, #$040E, #$045E,
87+ #$00B0, #$2219, #$00B7, #$221A, #$2116, #$00A4, #$25A0, #$00A0);
88+
89+function cp866Decode(Context: Pointer; InBuf: PChar; var InCnt: Cardinal; OutBuf: PWideChar;
90+ var OutCnt: Cardinal): Integer; stdcall;
91+var
92+ I: Integer;
93+ cnt: Cardinal;
94+begin
95+ cnt := OutCnt; // num of widechars
96+ if cnt > InCnt then
97+ cnt := InCnt;
98+ for I := 0 to cnt-1 do
99+ begin
100+ if InBuf[I] < #128 then
101+ OutBuf[I] := WideChar(ord(InBuf[I]))
102+ else
103+ OutBuf[I] := cp866table[InBuf[I]];
104+ end;
105+ Dec(InCnt, cnt);
106+ Dec(OutCnt, cnt);
107+ Result := cnt;
108+end;
109+
110+function GetCP866Decoder(const AEncoding: string; out Decoder: TDecoder): Boolean; stdcall;
111+begin
112+// Most encodings typically have one or more alias names.
113+ if SameText(AEncoding, 'IBM866') or
114+ SameText(AEncoding, 'cp866') or
115+ SameText(AEncoding, '866') or
116+ SameText(AEncoding, 'csIBM866') then
117+ begin
118+ Decoder.Decode := @cp866Decode;
119+ Decoder.Cleanup := nil;
120+ Decoder.Context := nil;
121+ Result := True;
122+ end
123+ else
124+ Result := False;
125+end;
126+
127+initialization
128+ RegisterDecoder(@GetCP866Decoder);
129+end.
130+```
131+
132+## See also
133+
134+- [[XML Tutorial]]
135+
136+---
137+*From the Free Pascal wiki, page "XML Decoders", as it stood on 2020-03-03 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history