Peter Siebel - Practical Common Lisp

Здесь есть возможность читать онлайн «Peter Siebel - Practical Common Lisp» весь текст электронной книги совершенно бесплатно (целиком полную версию без сокращений). В некоторых случаях можно слушать аудио, скачать через торрент в формате fb2 и присутствует краткое содержание. Год выпуска: 2005, ISBN: 2005, Издательство: Apress, Жанр: Программирование, на английском языке. Описание произведения, (предисловие) а так же отзывы посетителей доступны на портале библиотеки ЛибКат.

Practical Common Lisp: краткое содержание, описание и аннотация

Предлагаем к чтению аннотацию, описание, краткое содержание или предисловие (зависит от того, что написал сам автор книги «Practical Common Lisp»). Если вы не нашли необходимую информацию о книге — напишите в комментариях, мы постараемся отыскать её.

Practical Common Lisp — читать онлайн бесплатно полную книгу (весь текст) целиком

Ниже представлен текст книги, разбитый по страницам. Система сохранения места последней прочитанной страницы, позволяет с удобством читать онлайн бесплатно книгу «Practical Common Lisp», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

To handle this, you can define a binary type, id3-frames, that will be responsible for reading the remainder of a tag, creating frame objects to represent all the frames it finds, and then skipping over any padding. This type will take as a parameter the tag size, which it can use to avoid reading past the end of the tag. But the reading code will also need to detect the beginning of the padding that can follow the tag's frame data. Rather than calling read-valuedirectly in id3-frames :reader, you should use a function read-frame, which you'll define to return NIL when it detects padding, otherwise returning an id3-frameobject read using read-value. Assuming you define read-frameso it reads only one byte past the end of the last frame in order to detect the start of the padding, you can define the id3-framesbinary type like this:

(define-binary-type id3-frames (tag-size)

(:reader (in)

(loop with to-read = tag-size

while (plusp to-read)

for frame = (read-frame in)

while frame

do (decf to-read (+ 6 (size frame)))

collect frame

finally (loop repeat (1- to-read) do (read-byte in))))

(:writer (out frames)

(loop with to-write = tag-size

for frame in frames

do (write-value 'id3-frame out frame)

(decf to-write (+ 6 (size frame)))

finally (loop repeat to-write do (write-byte 0 out)))))

You can use this type to add a framesslot to id3-tag.

(define-binary-class id3-tag ()

((identifier (iso-8859-1-string :length 3))

(major-version u1)

(revision u1)

(flags u1)

(size id3-tag-size)

(frames (id3-frames :tag-size size))))

Detecting Tag Padding

Now all that remains is to implement read-frame. This is a bit tricky since the code that actually reads bytes from the stream is several layers down from read-frame.

What you'd really like to do in read-frameis read one byte and return NIL if it's a null and otherwise read a frame with read-value. Unfortunately, if you read the byte in read-frame, then it won't be available to be read by read-value. [276] Character streams support two functions, PEEK-CHAR and UNREAD-CHAR , either of which would be a perfect solution to this problem, but binary streams support no equivalent functions.

It turns out this is a perfect opportunity to use the condition system—you can check for null bytes in the low-level code that reads from the stream and signal a condition when you read a null; read-framecan then handle the condition by unwinding the stack before more bytes are read. In addition to turning out to be a tidy solution to the problem of detecting the start of the tag's padding, this is also an example of how you can use conditions for purposes other than handling errors.

You can start by defining a condition type to be signaled by the low-level code and handled by the high-level code. This condition doesn't need any slots—you just need a distinct class of condition so you know no other code will be signaling or handling it.

(define-condition in-padding () ())

Next you need to define a binary type whose :readerreads a given number of bytes, first reading a single byte and signaling an in-paddingcondition if the byte is null and otherwise reading the remaining bytes as an iso-8859-1-stringand combining it with the first byte read.

(define-binary-type frame-id (length)

(:reader (in)

(let ((first-byte (read-byte in)))

(when (= first-byte 0) (signal 'in-padding))

(let ((rest (read-value 'iso-8859-1-string in :length (1- length))))

(concatenate

'string (string (code-char first-byte)) rest))))

(:writer (out id)

(write-value 'iso-8859-1-string out id :length length)))

If you redefine id3-frameto make the type of its idslot frame-idinstead of iso-8859-1-string, the condition will be signaled whenever id3-frame's read-valuemethod reads a null byte instead of the beginning of a frame.

(define-tagged-binary-class id3-frame ()

((id (frame-id :length 3))

(size u3))

(:dispatch (find-frame-class id)))

Now all read-framehas to do is wrap a call to read-valuein a HANDLER-CASE that handles the in-paddingcondition by returning NIL .

(defun read-frame (in)

(handler-case (read-value 'id3-frame in)

(in-padding () nil)))

With read-framedefined, you can now read a complete version 2.2 ID3 tag, representing frames with instances of generic-frame. In the "What Frames Do You Actually Need?" section, you'll do some experiments at the REPL to determine what frame classes you need to implement. But first let's add support for version 2.3 ID3 tags.

Supporting Multiple Versions of ID3

Currently, id3-tagis defined using define-binary-class, but if you want to support multiple versions of ID3, it makes more sense to use a define-tagged-binary-classthat dispatches on the major-versionvalue. As it turns out, all versions of ID3v2 have the same structure up to the size field. So, you can define a tagged binary class like the following that defines this basic structure and then dispatches to the appropriate version-specific subclass:

(define-tagged-binary-class id3-tag ()

((identifier (iso-8859-1-string :length 3))

(major-version u1)

(revision u1)

(flags u1)

(size id3-tag-size))

(:dispatch

(ecase major-version

(2 'id3v2.2-tag)

(3 'id3v2.3-tag))))

Version 2.2 and version 2.3 tags differ in two ways. First, the header of a version 2.3 tag may be extended with up to four optional extended header fields, as determined by values in the flags field. Second, the frame format changed between version 2.2 and version 2.3, which means you'll have to use different classes to represent version 2.2 frames and the corresponding version 2.3 frames.

Since the new id3-tagclass is based on the one you originally wrote to represent version 2.2 tags, it's not surprising that the new id3v2.2-tagclass is trivial, inheriting most of its slots from the new id3-tagclass and adding the one missing slot, frames. Because version 2.2 and version 2.3 tags use different frame formats, you'll have to change the id3-framestype to be parameterized with the type of frame to read. For now, assume you'll do that and add a :frame-typeargument to the id3-framestype descriptor like this:

(define-binary-class id3v2.2-tag (id3-tag)

((frames (id3-frames :tag-size size :frame-type 'id3v2.2-frame))))

The id3v2.3-tagclass is slightly more complex because of the optional fields. The first three of the four optional fields are included when the sixth bit in flagsis set. They're a four- byte integer specifying the size of the extended header, two bytes worth of flags, and another four-byte integer specifying how many bytes of padding are included in the tag. [277] If a tag had an extended header, you could use this value to determine where the frame data should end. However, if the extended header isn't used, you'd have to use the old algorithm anyway, so it's not worth adding code to do it another way. The fourth optional field, included when the fifteenth bit of the extended header flags is set, is a four-byte cyclic redundancy check (CRC) of the rest of the tag.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Похожие книги на «Practical Common Lisp»

Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.


Отзывы о книге «Practical Common Lisp»

Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.

x