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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

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

Интервал:

Закладка:

Сделать

The binary data library doesn't provide any special support for optional fields in a binary class, but it turns out that regular parameterized binary types are sufficient. You can define a type parameterized with the name of a type and a value that indicates whether a value of that type should actually be read or written.

(define-binary-type optional (type if)

(:reader (in)

(when if (read-value type in)))

(:writer (out value)

(when if (write-value type out value))))

Using ifas the parameter name looks a bit strange in that code, but it makes the optionaltype descriptors quite readable. For instance, here's the definition of id3v2.3-tagusing optionalslots:

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

((extended-header-size (optional :type 'u4 :if (extended-p flags)))

(extra-flags (optional :type 'u2 :if (extended-p flags)))

(padding-size (optional :type 'u4 :if (extended-p flags)))

(crc (optional :type 'u4 :if (crc-p flags extra-flags)))

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

where extended-pand crc-pare helper functions that test the appropriate bit of the flags value they're passed. To test whether an individual bit of an integer is set, you can use LOGBITP , another bit-twiddling function. It takes an index and an integer and returns true if the specified bit is set in the integer.

(defun extended-p (flags) (logbitp 6 flags))

(defun crc-p (flags extra-flags)

(and (extended-p flags) (logbitp 15 extra-flags)))

As in the version 2.2 tag class, the frames slot is defined to be of type id3-frames, passing the name of the frame type as a parameter. You do, however, need to make a few small changes to id3-framesand read-frameto support the extra frame-typeparameter.

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

(:reader (in)

(loop with to-read = tag-size

while (plusp to-read)

for frame = (read-frame frame-type in)

while frame

do (decf to-read (+ (frame-header-size frame) (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 frame-type out frame)

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

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

(defun read-frame (frame-type in)

(handler-case (read-value frame-type in)

(in-padding () nil)))

The changes are in the calls to read-frameand write-value, where you need to pass the frame-typeargument and, in computing the size of the frame, where you need to use a function frame-header-sizeinstead of the literal value 6since the frame header changed size between version 2.2 and version 2.3. Since the difference in the result of this function is based on the class of the frame, it makes sense to define it as a generic function like this:

(defgeneric frame-header-size (frame))

You'll define the necessary methods on that generic function in the next section after you define the new frame classes.

Versioned Frame Base Classes

Where before you defined a single base class for all frames, you'll now have two classes, id3v2.2-frameand id3v2.3-frame. The id3v2.2-frameclass will be essentially the same as the original id3-frameclass.

(define-tagged-binary-class id3v2.2-frame ()

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

(size u3))

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

The id3v2.3-frame, on the other hand, requires more changes. The frame identifier and size fields were extended in version 2.3 from three to four bytes each, and two bytes worth of flags were added. Additionally, the frame, like the version 2.3 tag, can contain optional fields, controlled by the values of three of the frame's flags. [278] These flags, in addition to controlling whether the optional fields are included, can affect the parsing of the rest of the tag. In particular, if the seventh bit of the flags is set, then the actual frame data is compressed using the zlib algorithm, and if the sixth bit is set, the data is encrypted. In practice these options are rarely, if ever, used, so you can get away with ignoring them for now. But that would be an area you'd have to address to make this a production-quality ID3 library. One simple half solution would be to change find-frame-class to accept a second argument and pass it the flags; if the frame is compressed or encrypted, you could instantiate a generic frame to hold the data. With those changes in mind, you can define the version 2.3 frame base class, along with some helper functions, like this:

(define-tagged-binary-class id3v2.3-frame ()

((id (frame-id :length 4))

(size u4)

(flags u2)

(decompressed-size (optional :type 'u4 :if (frame-compressed-p flags)))

(encryption-scheme (optional :type 'u1 :if (frame-encrypted-p flags)))

(grouping-identity (optional :type 'u1 :if (frame-grouped-p flags))))

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

(defun frame-compressed-p (flags) (logbitp 7 flags))

(defun frame-encrypted-p (flags) (logbitp 6 flags))

(defun frame-grouped-p (flags) (logbitp 5 flags))

With these two classes defined, you can now implement the methods on the generic function frame-header-size.

(defmethod frame-header-size ((frame id3v2.2-frame)) 6)

(defmethod frame-header-size ((frame id3v2.3-frame)) 10)

The optional fields in a version 2.3 frame aren't counted as part of the header for this computation since they're already included in the value of the frame's size.

Versioned Concrete Frame Classes

In the original definition, generic-framesubclassed id3-frame. But now id3-framehas been replaced with the two version-specific base classes, id3v2.2-frameand id3v2.3-frame. So, you need to define two new versions of generic-frame, one for each base class. One way to define this classes would be like this:

(define-binary-class generic-frame-v2.2 (id3v2.2-frame)

((data (raw-bytes :size size))))

(define-binary-class generic-frame-v2.3 (id3v2.3-frame)

((data (raw-bytes :size size))))

However, it's a bit annoying that these two classes are the same except for their superclass. It's not too bad in this case since there's only one additional field. But if you take this approach for other concrete frame classes, ones that have a more complex internal structure that's identical between the two ID3 versions, the duplication will be more irksome.

Another approach, and the one you should actually use, is to define a class generic-frameas a mixin : a class intended to be used as a superclass along with one of the version-specific base classes to produce a concrete, version-specific frame class. The only tricky bit about this approach is that if generic-framedoesn't extend either of the frame base classes, then you can't refer to the sizeslot in its definition. Instead, you must use the current-binary-objectfunction I discussed at the end of the previous chapter to access the object you're in the midst of reading or writing and pass it to size. And you need to account for the difference in the number of bytes of the total frame size that will be left over, in the case of a version 2.3 frame, if any of the optional fields are included in the frame. So, you should define a generic function data-byteswith methods that do the right thing for both version 2.2 and version 2.3 frames.

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

Интервал:

Закладка:

Сделать

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

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


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

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

x