@@ -29,6 +29,7 @@ const magic_func = Array(Pair, 0) # for formats with complex magic #s
29
29
`add_format(fmt, magic, extention)` registers a new `DataFormat`.
30
30
For example:
31
31
32
+ add_format(format"PNG", (UInt8[0x4d,0x4d,0x00,0x2b], UInt8[0x49,0x49,0x2a,0x00]), [".tiff", ".tif"])
32
33
add_format(format"PNG", [0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a], ".png")
33
34
add_format(format"NRRD", "NRRD", [".nrrd",".nhdr"])
34
35
@@ -47,6 +48,22 @@ function add_format{sym}(fmt::Type{DataFormat{sym}}, magic::Union(Tuple,Abstract
47
48
fmt
48
49
end
49
50
51
+ # for multiple magic bytes
52
+ function add_format {sym, T <: Vector{Uint8}, N} (fmt:: Type{DataFormat{sym}} , magics:: NTuple{N, T} , extension)
53
+ haskey (sym2info, sym) && error (" format " , fmt, " is already registered" )
54
+ magics = map (canonicalize_magic, magics)
55
+ for magic in magics
56
+ rng = searchsorted (magic_list, magic, lt= magic_cmp)
57
+ if ! isempty (magic) && ! isempty (rng)
58
+ error (" magic bytes " , magic, " are already registered" )
59
+ end
60
+ insert! (magic_list, first (rng), Pair (magic, sym)) # m=>sym in 0.4
61
+ end
62
+ sym2info[sym] = (magics, extension)
63
+ add_extension (extension, sym)
64
+ fmt
65
+ end
66
+
50
67
# For when "magic" is supplied as a function (see the HDF5 example in
51
68
# registry.jl)
52
69
function add_format {sym} (fmt:: Type{DataFormat{sym}} , magic, extension)
62
79
""" ->
63
80
function del_format {sym} (fmt:: Type{DataFormat{sym}} )
64
81
magic, extension = sym2info[sym]
82
+ del_magic (magic, sym)
83
+ delete! (sym2info, sym)
84
+ del_extension (extension)
85
+ nothing
86
+ end
87
+
88
+ # Deletes mutliple magic bytes
89
+ del_magic (magic:: Tuple , sym) = for m in magic
90
+ del_magic (m, sym)
91
+ end
92
+ # Deletes single magic bytes
93
+ function del_magic {N} (magic:: NTuple{N, Uint8} , sym)
65
94
rng = searchsorted (magic_list, magic, lt= magic_cmp)
66
95
if length (magic) == 0
67
96
fullrng = rng
@@ -77,8 +106,6 @@ function del_format{sym}(fmt::Type{DataFormat{sym}})
77
106
end
78
107
@assert length (rng) == 1
79
108
deleteat! (magic_list, first (rng))
80
- delete! (sym2info, sym)
81
- del_extension (extension)
82
109
nothing
83
110
end
84
111
@@ -242,11 +269,37 @@ For a plain IO object, you can use `skipmagic(io, fmt)`.
242
269
skipmagic {F} (s:: Stream{F} ) = (skipmagic (stream (s), F); s)
243
270
function skipmagic {sym} (io, fmt:: Type{DataFormat{sym}} )
244
271
magic, _ = sym2info[sym]
245
- if ! isa (magic, Function)
246
- seek (io, length (magic))
272
+ skipmagic (io, magic)
273
+ nothing
274
+ end
275
+ skipmagic (io, magic:: Function ) = nothing
276
+ skipmagic {N} (io, magic:: NTuple{N,UInt8} ) = seek (io, length (magic))
277
+ function skipmagic (io, magic:: Tuple )
278
+ lengths = map (length, magic)
279
+ all (x-> lengths[1 ] == x, lengths) && return seek (io, lengths[1 ]) # it doesn't matter what magic bytes get skipped as they all have the same length
280
+ magic = [magic... ]
281
+ sort! (magic, lt= (a,b)-> length (a)>= length (b)) # start with longest first, to avoid overlapping magic bytes
282
+ seekend (io)
283
+ len = position (io)
284
+ seekstart (io)
285
+ filter! (x-> length (x) <= len, magic) # throw out magic bytes that are longer than IO
286
+ tmp = readbytes (io, length (first (magic))) # now, first is both the longest and guaranteed to fit into io, so we can just read the bytes
287
+ for m in magic
288
+ if magic_equal (m, tmp)
289
+ seek (io, length (m))
290
+ return nothing
291
+ end
247
292
end
293
+ error (" tried to skip magic bytes of an IO that does not contain the magic bytes of the format. IO: $io " )
294
+ end
295
+ function magic_equal (magic, buffer)
296
+ for (i,elem) in enumerate (magic)
297
+ buffer[i] != elem && return false
298
+ end
299
+ true
248
300
end
249
301
302
+
250
303
unknown {F} (:: File{F} ) = unknown (F)
251
304
unknown {F} (:: Stream{F} ) = unknown (F)
252
305
@@ -257,13 +310,13 @@ function query(filename::AbstractString)
257
310
_, ext = splitext (filename)
258
311
if haskey (ext2sym, ext)
259
312
sym = ext2sym[ext]
260
- len = lenmagic (sym)
261
- if length (len ) == 1 && (all (x -> x == 0 , len) || ! isfile (filename)) # we only found one candidate and there is no magic bytes, or no file, trust the extension
313
+ no_magic = ! hasmagic (sym)
314
+ if lensym (sym ) == 1 && (no_magic || ! isfile (filename)) # we only found one candidate and there is no magic bytes, or no file, trust the extension
262
315
return File {DataFormat{sym}} (filename)
263
- elseif ! isfile (filename) && length (len ) > 1
316
+ elseif ! isfile (filename) && lensym (sym ) > 1
264
317
error (" no file for check of magic bytes and multiple extensions possible: $sym " )
265
318
end
266
- if any (x -> x == 0 , len )
319
+ if no_magic && ! hasfunction (sym )
267
320
error (" Some formats with extension " , ext, " have no magic bytes; use `File{format\" FMT\" }(filename)` to resolve the ambiguity." )
268
321
end
269
322
end
@@ -272,11 +325,19 @@ function query(filename::AbstractString)
272
325
file! (query (open (filename), filename))
273
326
end
274
327
275
- lenmagic (s:: Symbol ) = lenm (sym2info[s][1 ])
276
- lenmagic (v:: Vector ) = map (lenmagic, v)
328
+ lensym (s:: Symbol ) = 1
329
+ lensym (v:: Vector ) = length (v)
330
+
331
+ hasmagic (s:: Symbol ) = hasmagic (sym2info[s][1 ])
332
+ hasmagic (v:: Vector ) = any (hasmagic, v)
333
+
334
+ hasmagic (t:: Tuple ) = ! isempty (t)
335
+ hasmagic (:: Any ) = false # for when magic is a function
277
336
278
- lenm (t:: Tuple ) = length (t)
279
- lenm (:: Any ) = - 1 # for when magic is a function
337
+ hasfunction (s:: Symbol ) = hasfunction (sym2info[s][1 ])
338
+ hasfunction (v:: Vector ) = any (hasfunction, v)
339
+ hasfunction (s:: Any ) = true # has function
340
+ hasfunction (s:: Tuple ) = false # has magic
280
341
281
342
@doc """
282
343
`query(io, [filename])` returns a `Stream` object with information about the
0 commit comments