b7430c8b6b16103a4c971251ebccb4f7838573c5
[IRC.git] / Robust / src / Tests / ssJava / mp3decoder / Bitstream.java
1 /*
2  * 11/19/04  1.0 moved to LGPL.
3  * 
4  * 11/17/04     Uncomplete frames discarded. E.B, javalayer@javazoom.net 
5  *
6  * 12/05/03     ID3v2 tag returned. E.B, javalayer@javazoom.net 
7  *
8  * 12/12/99     Based on Ibitstream. Exceptions thrown on errors,
9  *              Temporary removed seek functionality. mdm@techie.com
10  *
11  * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
12  *
13  * 04/14/97 : Added function prototypes for new syncing and seeking
14  * mechanisms. Also made this file portable. Changes made by Jeff Tsay
15  *
16  *  @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
17  *  @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
18  *  @(#) Berlin University of Technology
19  *-----------------------------------------------------------------------
20  *   This program is free software; you can redistribute it and/or modify
21  *   it under the terms of the GNU Library General Public License as published
22  *   by the Free Software Foundation; either version 2 of the License, or
23  *   (at your option) any later version.
24  *
25  *   This program is distributed in the hope that it will be useful,
26  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  *   GNU Library General Public License for more details.
29  *
30  *   You should have received a copy of the GNU Library General Public
31  *   License along with this program; if not, write to the Free Software
32  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33  *----------------------------------------------------------------------
34  */
35
36 /**
37  * The <code>Bistream</code> class is responsible for parsing an MPEG audio
38  * bitstream.
39  * 
40  * <b>REVIEW:</b> much of the parsing currently occurs in the various decoders.
41  * This should be moved into this class and associated inner classes.
42  */
43 @LATTICE("FB<F,FF<F,WP<BI,FF*,WP*,BI*")
44 @METHODDEFAULT("OUT<THIS,THIS<VAR,VAR<IN,VAR*,THISLOC=THIS,GLOBALLOC=IN")
45 public final class Bitstream implements BitstreamErrors {
46   /**
47    * Synchronization control constant for the initial synchronization to the
48    * start of a frame.
49    */
50   @LOC("F")
51   static byte INITIAL_SYNC = 0;
52
53   /**
54    * Synchronization control constant for non-initial frame synchronizations.
55    */
56
57   @LOC("F")
58   static byte STRICT_SYNC = 1;
59
60   // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
61   /**
62    * Maximum size of the frame buffer.
63    */
64   @LOC("F")
65   private static final int BUFFER_INT_SIZE = 433;
66
67   /**
68    * The frame buffer that holds the data for the current frame.
69    */
70   @LOC("FB")
71   private final int[] framebuffer = new int[BUFFER_INT_SIZE];
72
73   /**
74    * Number of valid bytes in the frame buffer.
75    */
76   @LOC("F")
77   private int framesize;
78
79   /**
80    * The bytes read from the stream.
81    */
82   @LOC("FB")
83   private byte[] frame_bytes = new byte[BUFFER_INT_SIZE * 4];
84
85   /**
86    * Index into <code>framebuffer</code> where the next bits are retrieved.
87    */
88   @LOC("WP")
89   private int wordpointer;
90
91   /**
92    * Number (0-31, from MSB to LSB) of next bit for get_bits()
93    */
94   @LOC("BI")
95   private int bitindex;
96
97   /**
98    * The current specified syncword
99    */
100   @LOC("F")
101   private int syncword;
102
103   /**
104    * Audio header position in stream.
105    */
106   @LOC("F")
107   private int header_pos = 0;
108
109   /**
110       *
111       */
112   @LOC("F")
113   private boolean single_ch_mode;
114   // private int current_frame_number;
115   // private int last_frame_number;
116
117   @LOC("F")
118   private final int bitmask[] = {
119       0, // dummy
120       0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
121       0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF,
122       0x00007FFF, 0x0000FFFF, 0x0001FFFF };
123
124   @LOC("F")
125   private final PushbackInputStream source;
126
127   @LOC("F")
128   private final Header header = new Header();
129
130   @LOC("F")
131   private final byte syncbuf[] = new byte[4];
132
133   @LOC("F")
134   private Crc16[] crc = new Crc16[1];
135
136   @LOC("F")
137   private byte[] rawid3v2 = null;
138
139   @LOC("FF")
140   private boolean firstframe = true;
141
142   private BitReserve br;
143   private int main_data_begin;
144   private int frame_start;
145
146   /**
147    * Construct a IBitstream that reads data from a given InputStream.
148    * 
149    * @param in
150    *          The InputStream to read from.
151    */
152   public Bitstream(InputStream in) {
153     if (in == null)
154       throw new NullPointerException("in");
155     in = new BufferedInputStream(in);
156     loadID3v2(in);
157     firstframe = true;
158     // source = new PushbackInputStream(in, 1024);
159     source = new PushbackInputStream(in, BUFFER_INT_SIZE * 4);
160
161     closeFrame();
162     // current_frame_number = -1;
163     // last_frame_number = -1;
164
165     br = new BitReserve();
166
167   }
168
169   /**
170    * Return position of the first audio header.
171    * 
172    * @return size of ID3v2 tag frames.
173    */
174   public int header_pos() {
175     return header_pos;
176   }
177
178   /**
179    * Load ID3v2 frames.
180    * 
181    * @param in
182    *          MP3 InputStream.
183    * @author JavaZOOM
184    */
185   private void loadID3v2(InputStream in) {
186     int size = -1;
187     try {
188       // Read ID3v2 header (10 bytes).
189       in.mark(10);
190       size = readID3v2Header(in);
191       header_pos = size;
192     } catch (IOException e) {
193     } finally {
194       try {
195         // Unread ID3v2 header (10 bytes).
196         in.reset();
197       } catch (IOException e) {
198       }
199     }
200     // Load ID3v2 tags.
201     try {
202       if (size > 0) {
203         rawid3v2 = new byte[size];
204         in.read(rawid3v2, 0, rawid3v2.length);
205       }
206     } catch (IOException e) {
207     }
208   }
209
210   /**
211    * Parse ID3v2 tag header to find out size of ID3v2 frames.
212    * 
213    * @param in
214    *          MP3 InputStream
215    * @return size of ID3v2 frames + header
216    * @throws IOException
217    * @author JavaZOOM
218    */
219   private int readID3v2Header(InputStream in) throws IOException {
220     byte[] id3header = new byte[4];
221     int size = -10;
222     in.read(id3header, 0, 3);
223     // Look for ID3v2
224     if ((id3header[0] == 'I') && (id3header[1] == 'D') && (id3header[2] == '3')) {
225       in.read(id3header, 0, 3);
226       int majorVersion = id3header[0];
227       int revision = id3header[1];
228       in.read(id3header, 0, 4);
229       size =
230           (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
231     }
232     return (size + 10);
233   }
234
235   /**
236    * Return raw ID3v2 frames + header.
237    * 
238    * @return ID3v2 InputStream or null if ID3v2 frames are not available.
239    */
240   public InputStream getRawID3v2() {
241     if (rawid3v2 == null)
242       return null;
243     else {
244       ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
245       return bain;
246     }
247   }
248
249   /**
250    * Close the Bitstream.
251    * 
252    * @throws BitstreamException
253    */
254   public void close() throws BitstreamException {
255     try {
256       source.close();
257     } catch (IOException ex) {
258       throw newBitstreamException(STREAM_ERROR, ex);
259     }
260   }
261
262   /**
263    * Reads and parses the next frame from the input source.
264    * 
265    * @return the Header describing details of the frame read, or null if the end
266    *         of the stream has been reached.
267    */
268   public Header readFrame() throws BitstreamException {
269     
270     System.out.println("ST");
271     
272     Header result = null;
273     try {
274       System.out.println("HERE?");
275       result = readNextFrame();
276       System.out.println("HERE?2");
277       // E.B, Parse VBR (if any) first frame.
278       if (firstframe == true) {
279         result.parseVBR(frame_bytes);
280         firstframe = false;
281       }
282       
283       int channels = (header.mode() == Header.SINGLE_CHANNEL) ? 1 : 2;
284       result.setSideInfoBuf(getSideInfoBuffer(channels));
285       result.setBitReserve(getBitReserve(result.slots()));
286       
287     } catch (BitstreamException ex) {
288       if ((ex.getErrorCode() == INVALIDFRAME)) {
289         // Try to skip this frame.
290         // System.out.println("INVALIDFRAME");
291         try {    System.out.println("ET");
292
293           closeFrame();
294           result = readNextFrame();
295         } catch (BitstreamException e) {
296           if ((e.getErrorCode() != STREAM_EOF)) {
297             // wrap original exception so stack trace is maintained.
298             throw newBitstreamException(e.getErrorCode(), e);
299           }
300         }
301       } else if ((ex.getErrorCode() != STREAM_EOF)) {
302         // wrap original exception so stack trace is maintained.
303         throw newBitstreamException(ex.getErrorCode(), ex);
304       }
305     }
306     System.out.println("EN");
307
308     return result;
309   }
310
311   /**
312    * Read next MP3 frame.
313    * 
314    * @return MP3 frame header.
315    * @throws BitstreamException
316    */
317   private Header readNextFrame() throws BitstreamException {
318     System.out.println("framesize="+framesize);
319     if (framesize == -1) {
320       nextFrame();
321     }
322     return header;
323   }
324
325   /**
326    * Read next MP3 frame.
327    * 
328    * @throws BitstreamException
329    */
330   private void nextFrame() throws BitstreamException {
331     // entire frame is read by the header class.
332     System.out.println("nextFrame()");
333     header.read_header(this, crc);
334   }
335
336   /**
337    * Unreads the bytes read from the frame.
338    * 
339    * @throws BitstreamException
340    */
341   // REVIEW: add new error codes for this.
342   public void unreadFrame() throws BitstreamException {
343     if (wordpointer == -1 && bitindex == -1 && (framesize > 0)) {
344       try {
345         source.unread(frame_bytes, 0, framesize);
346       } catch (IOException ex) {
347         throw newBitstreamException(STREAM_ERROR);
348       }
349     }
350   }
351
352   /**
353    * Close MP3 frame.
354    */
355   public void closeFrame() {
356     framesize = -1;
357     wordpointer = -1;
358     bitindex = -1;
359   }
360
361   /**
362    * Determines if the next 4 bytes of the stream represent a frame header.
363    */
364   public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException {
365     int read = readBytes(syncbuf, 0, 4);
366     int headerstring =
367         ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000)
368             | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
369
370     try {
371       source.unread(syncbuf, 0, read);
372     } catch (IOException ex) {
373     }
374
375     boolean sync = false;
376     switch (read) {
377     case 0:
378       sync = true;
379       break;
380     case 4:
381       sync = isSyncMark(headerstring, syncmode, syncword);
382       break;
383     }
384
385     return sync;
386   }
387
388   // REVIEW: this class should provide inner classes to
389   // parse the frame contents. Eventually, readBits will
390   // be removed.
391   public int readBits(int n) {
392     return get_bits(n);
393   }
394
395   public int peek_bits(int number_of_bits) {
396
397     int peekbitindex = bitindex;
398     int peekPointer = wordpointer;
399
400     int returnvalue = 0;
401     int sum = peekbitindex + number_of_bits;
402
403     if (peekPointer < 0) {
404       peekPointer = 0;
405     }
406
407     if (sum <= 32) {
408       // all bits contained in *wordpointer
409       returnvalue = (framebuffer[peekPointer] >>> (32 - sum)) & bitmask[number_of_bits];
410       // returnvalue = (wordpointer[0] >> (32 - sum)) &
411       // bitmask[number_of_bits];
412       if ((peekbitindex += number_of_bits) == 32) {
413         peekbitindex = 0;
414         peekPointer++; // added by me!
415       }
416       return returnvalue;
417     }
418
419     // E.B : Check that ?
420     // ((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
421     // wordpointer++; // Added by me!
422     // ((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
423     int Right = (framebuffer[peekPointer] & 0x0000FFFF);
424     peekPointer++;
425     int Left = (framebuffer[peekPointer] & 0xFFFF0000);
426     returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16) & 0x0000FFFF);
427
428     returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32
429                                // - bitindex))
430     returnvalue &= bitmask[number_of_bits];
431     peekbitindex = sum - 32;
432     return returnvalue;
433
434   }
435
436   public int readCheckedBits(int n) {
437     // REVIEW: implement CRC check.
438     return get_bits(n);
439   }
440
441   protected BitstreamException newBitstreamException(int errorcode) {
442     return new BitstreamException(errorcode, null);
443   }
444
445   protected BitstreamException newBitstreamException(int errorcode, Throwable throwable) {
446     return new BitstreamException(errorcode, throwable);
447   }
448
449   /**
450    * Get next 32 bits from bitstream. They are stored in the headerstring.
451    * syncmod allows Synchro flag ID The returned value is False at the end of
452    * stream.
453    */
454
455   int syncHeader(byte syncmode) throws BitstreamException {
456     boolean sync;
457     int headerstring;
458     // read additional 2 bytes
459     int bytesRead = readBytes(syncbuf, 0, 3);
460
461     if (bytesRead != 3)
462       throw newBitstreamException(STREAM_EOF, null);
463
464     headerstring =
465         ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00)
466             | ((syncbuf[2] << 0) & 0x000000FF);
467
468     do {
469       headerstring <<= 8;
470
471       if (readBytes(syncbuf, 3, 1) != 1)
472         throw newBitstreamException(STREAM_EOF, null);
473
474       headerstring |= (syncbuf[3] & 0x000000FF);
475
476       sync = isSyncMark(headerstring, syncmode, syncword);
477     } while (!sync);
478
479     // current_frame_number++;
480     // if (last_frame_number < current_frame_number) last_frame_number =
481     // current_frame_number;
482
483     return headerstring;
484   }
485
486   public boolean isSyncMark(int headerstring, int syncmode, int word) {
487     boolean sync = false;
488
489     if (syncmode == INITIAL_SYNC) {
490       // sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
491       sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
492     } else {
493       sync =
494           ((headerstring & 0xFFF80C00) == word)
495               && (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
496     }
497
498     // filter out invalid sample rate
499     if (sync)
500       sync = (((headerstring >>> 10) & 3) != 3);
501     // filter out invalid layer
502     if (sync)
503       sync = (((headerstring >>> 17) & 3) != 0);
504     // filter out invalid version
505     if (sync)
506       sync = (((headerstring >>> 19) & 3) != 1);
507
508     return sync;
509   }
510
511   /**
512    * Reads the data for the next frame. The frame is not parsed until parse
513    * frame is called.
514    */
515   int read_frame_data(int bytesize) throws BitstreamException {
516     int numread = 0;
517     numread = readFully(frame_bytes, 0, bytesize);
518     framesize = bytesize;
519     wordpointer = -1;
520     bitindex = -1;
521     return numread;
522   }
523
524   /**
525    * Parses the data previously read with read_frame_data().
526    */
527   @LATTICE("GLOBAL<B,B<BNUM,BNUM<K,K<BYTE,BYTE<THIS,B*,K*,THISLOC=THIS,GLOBALLOC=GLOBAL")
528   void parse_frame() throws BitstreamException {
529     // Convert Bytes read to int
530     @LOC("B") int b = 0;
531     @LOC("BYTE") byte[] byteread = frame_bytes;
532     @LOC("BYTE") int bytesize = framesize;
533
534     // Check ID3v1 TAG (True only if last frame).
535     // for (int t=0;t<(byteread.length)-2;t++)
536     // {
537     // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
538     // {
539     // System.out.println("ID3v1 detected at offset "+t);
540     // throw newBitstreamException(INVALIDFRAME, null);
541     // }
542     // }
543
544     for (@LOC("K") int k = 0; k < bytesize; k = k + 4) {
545       @LOC("BYTE") int convert = 0;
546       @LOC("BNUM") byte b0 = 0;
547       @LOC("BNUM") byte b1 = 0;
548       @LOC("BNUM") byte b2 = 0;
549       @LOC("BNUM") byte b3 = 0;
550       b0 = byteread[k];
551       if (k + 1 < bytesize)
552         b1 = byteread[k + 1];
553       if (k + 2 < bytesize)
554         b2 = byteread[k + 2];
555       if (k + 3 < bytesize)
556         b3 = byteread[k + 3];
557       framebuffer[b++] =
558           ((b0 << 24) & 0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00)
559               | (b3 & 0x000000FF);
560     }
561     wordpointer = 0;
562     bitindex = 0;
563   }
564
565   /**
566    * Read bits from buffer into the lower bits of an unsigned int. The LSB
567    * contains the latest read bit of the stream. (1 <= number_of_bits <= 16)
568    */
569   @LATTICE("OUT<RL,RL<THIS,THIS<IN,OUT*,THISLOC=THIS,RETURNLOC=OUT")
570   public int get_bits(@LOC("IN") int number_of_bits) {
571
572     @LOC("OUT") int returnvalue = 0;
573     @LOC("THIS,Bitstream.BI") int sum = bitindex + number_of_bits;
574
575     // E.B
576     // There is a problem here, wordpointer could be -1 ?!
577     if (wordpointer < 0)
578       wordpointer = 0;
579     // E.B : End.
580
581     if (sum <= 32) {
582       // all bits contained in *wordpointer
583       returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];
584       // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];
585       if ((bitindex += number_of_bits) == 32) {
586         bitindex = 0;
587         wordpointer++; // added by me!
588       }
589       return returnvalue;
590     }
591
592     // E.B : Check that ?
593     // ((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
594     // wordpointer++; // Added by me!
595     // ((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
596     @LOC("RL") int Right = (framebuffer[wordpointer] & 0x0000FFFF);
597     wordpointer++;
598     @LOC("RL") int Left = (framebuffer[wordpointer] & 0xFFFF0000);
599     returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16) & 0x0000FFFF);
600
601     returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 -
602                                // bitindex))
603     returnvalue &= bitmask[number_of_bits];
604     bitindex = sum - 32;
605     return returnvalue;
606   }
607
608   /**
609    * Set the word we want to sync the header to. In Big-Endian byte order
610    */
611   void set_syncword(@LOC("IN") int syncword0) {
612     syncword = syncword0 & 0xFFFFFF3F;
613     single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
614   }
615
616   /**
617    * Reads the exact number of bytes from the source input stream into a byte
618    * array.
619    * 
620    * @param b
621    *          The byte array to read the specified number of bytes into.
622    * @param offs
623    *          The index in the array where the first byte read should be stored.
624    * @param len
625    *          the number of bytes to read.
626    * 
627    * @exception BitstreamException
628    *              is thrown if the specified number of bytes could not be read
629    *              from the stream.
630    */
631   @LATTICE("OUT<VAR,VAR<THIS,THIS<IN,IN*,VAR*,THISLOC=THIS")
632   @RETURNLOC("OUT")
633   private int readFully(@LOC("OUT") byte[] b, @LOC("IN") int offs, @LOC("IN") int len)
634       throws BitstreamException {
635     @LOC("VAR") int nRead = 0;
636     try {
637       while (len > 0) {
638         @LOC("IN") int bytesread = source.read(b, offs, len);
639         if (bytesread == -1) {
640           while (len-- > 0) {
641             b[offs++] = 0;
642           }
643           break;
644           // throw newBitstreamException(UNEXPECTED_EOF, new EOFException());
645         }
646         nRead = nRead + bytesread;
647         offs += bytesread;
648         len -= bytesread;
649       }
650     } catch (IOException ex) {
651       throw newBitstreamException(STREAM_ERROR, ex);
652     }
653     return nRead;
654   }
655
656   /**
657    * Simlar to readFully, but doesn't throw exception when EOF is reached.
658    */
659   @LATTICE("OUT<VAR,VAR<THIS,THIS<IN,IN*,VAR*,THISLOC=THIS")
660   @RETURNLOC("OUT")
661   private int readBytes(@LOC("OUT") byte[] b, @LOC("IN") int offs, @LOC("IN") int len)
662       throws BitstreamException {
663     @LOC("VAR") int totalBytesRead = 0;
664     try {
665       while (len > 0) {
666         @LOC("IN") int bytesread = source.read(b, offs, len);
667         if (bytesread == -1) {
668           break;
669         }
670         totalBytesRead += bytesread;
671         offs += bytesread;
672         len -= bytesread;
673       }
674     } catch (IOException ex) {
675       throw newBitstreamException(STREAM_ERROR, ex);
676     }
677     return totalBytesRead;
678   }
679
680   public SideInfoBuffer getSideInfoBuffer(int channelType) {
681
682     if (wordpointer < 0)
683       wordpointer = 0;
684
685     SideInfoBuffer sib = new SideInfoBuffer();
686
687     // first, store main_data_begin from the side inforamtion
688     main_data_begin = peek_bits(9);
689     System.out.println("main_data_begin=" + main_data_begin);
690
691     int max;
692     if (channelType == 1) { // mono
693       max = wordpointer + 4;
694     } else {
695       max = wordpointer + 8;
696     }
697
698     try {
699       for (; wordpointer < max; wordpointer++) {
700         sib.setBuffer(wordpointer, framebuffer[wordpointer]);
701       }
702     } catch (ArrayIndexOutOfBoundsException e) {
703       System.out.print("wordpointer=" + wordpointer);
704       System.out.println("framebuffer length=" + framebuffer.length);
705     }
706
707     return sib;
708   }
709
710   public BitReserve getBitReserve(int nSlots) {
711
712     int flush_main;
713     int bytes_to_discard;
714     int i;
715
716     for (i = 0; i < nSlots; i++)
717       br.hputbuf(get_bits(8));
718
719     int main_data_end = br.hsstell() >>> 3; // of previous frame
720
721     if ((flush_main = (br.hsstell() & 7)) != 0) {
722       br.hgetbits(8 - flush_main);
723       main_data_end++;
724     }
725
726     bytes_to_discard = frame_start - main_data_end - main_data_begin;
727
728     frame_start += nSlots;
729
730     if (bytes_to_discard < 0) {
731       System.out.println("HERE?");
732       return null;
733     }
734
735     if (main_data_end > 4096) {
736       frame_start -= 4096;
737       br.rewindNbytes(4096);
738     }
739
740     for (; bytes_to_discard > 0; bytes_to_discard--)
741       br.hgetbits(8);
742
743     return br;
744   }
745 }