- create go.mod (deleted upstream at v1.0.3) with module github.com/hanzoai/goexif, go 1.21
- rewrite all internal self-imports (exif/tiff/mknote/exifstat/tests/regen) to hanzoai path
- update README import-path/install references
- pure stdlib, zero external deps; go build/vet/test all pass
go: github.com/seaweedfs/goexif@v2.0.0: invalid version: module contains a go.mod file, so module path must match major version ("github.com/seaweedfs/goexif/v2")
* Add benchmark test
Here are the results ran locally on a SSD:
goos: darwin
goarch: amd64
pkg: github.com/rwcarlsen/goexif/tiff
BenchmarkDecode-8 Skip downloading existing raw file http://www.rawsamples.ch/raws/canon/RAW_CANON_EOS_5DS.CR2
200 43701603 ns/op 268632324 B/op 8709 allocs/op
PASS
ok github.com/rwcarlsen/goexif/tiff 10.294s
Signed-off-by: Thibault Jamet <tjamet@users.noreply.github.com>
* Implement a lazy loader
When using tiff files like raw files, the time required to load the
whole file is significant. We do not load the whole picture frame in-memory
in order to access EXIF values. We do not even need to load all
EXIF values when we need to access a single EXIF field.
To improve the tiff loading speed, opportustically read from the
source file when needed.
This is a significant change in the interface as it requires to pass a
ReaderAt instead of a Reader.
In order not to break backward compatibility, a LazyDecode function has
been implemented to keep the same interface for the Decode one,
including filling every Tag.
There are 2 places where the backward compatibility is broken:
- DecodeTag now requires a ReaderAt and the base offset of the tag
- In case of using an open file to feed the tiff decoder, the file needs
to stay open during the whole life cycle of the tiff structure
Benchmark results (on SSD):
EXIF parsing
goos: darwin
goarch: amd64
pkg: github.com/rwcarlsen/goexif/exif
BenchmarkDecode-8 200000 89622 ns/op 133531 B/op 236 allocs/op
BenchmarkDecodeRaw-8 30000 486907 ns/op 341765 B/op 8553 allocs/op
BenchmarkLazyDecode-8 200000 83383 ns/op 31659 B/op 151 allocs/op
BenchmarkLazyDecodeRaw-8 200000 81325 ns/op 25051 B/op 164 allocs/op
PASS
ok github.com/rwcarlsen/goexif/exif 72.727s
Before update, the benchmark results for exif parsing were:
goos: darwin
goarch: amd64
pkg: github.com/rwcarlsen/goexif/exif
BenchmarkDecode-8 100000 170961 ns/op 342412 B/op 961 allocs/op
BenchmarkDecodeRaw-8 100 109398876 ns/op 750784550 B/op 9244 allocs/op
PASS
ok github.com/rwcarlsen/goexif/exif 33.940s
TIFF decoding
goos: darwin
goarch: amd64
pkg: github.com/rwcarlsen/goexif/tiff
BenchmarkDecode-8 300 46751365 ns/op 268452505 B/op 113 allocs/op
BenchmarkLazyDecode-8 300000 43886 ns/op 10645 B/op 82 allocs/op
PASS
ok github.com/rwcarlsen/goexif/tiff 32.337s
Signed-off-by: Thibault Jamet <tjamet@users.noreply.github.com>
* Add test to verify that Decode loads tag values
Future commits will use lazy load for values.
For backward compatibility, Decode will keep loading all tag values.
Signed-off-by: Thibault Jamet <tjamet@users.noreply.github.com>
* Add a test to ensure exif.Raw contains a valid exif file
Signed-off-by: Thibault Jamet <tjamet@users.noreply.github.com>
* tweaks
As it can apparently happen with some camera software
(https://user-images.githubusercontent.com/2621/39392188-71a66fc4-4a66-11e8-9e3b-694163efa643.jpg),
sometimes a tag bytes will not only have a bunch of trailing NULLs, but
the Count for the data in that tag will be wrongly set too. i.e. instead
of being set to the actual number of relevant bytes to read, it will
be set to the full number of bytes, trailing zeroes included.
As a consequence, the Tag.strVal returned would include these trailing
NULLs too, which would lead to bugs such as
https://github.com/perkeep/perkeep/issues/1127
This change therefore makes sure to remove all trailing NULLs from the
data, in the case of DTAscii at least.
exif: Define all constants as FieldName
Previously only the first entry was defined as a FieldName, the rest
were regular strings. Fix constant types in mknote and tiff packages correspondingly.
It can apparently happen (with at least one gimp .xcf file), that it has
all the same markers as in a jpeg, up until reading the 2 bytes that would
code for the data length.
Since the data length is supposed to include those 2 bytes, the code was
assuming that the value of data length would be always at least 3, and
slicing under that assumption. Which breaks when e.g. an .xcf file
encodes the value 1 on these 2 bytes.
Fixes#34
Context: https://code.google.com/p/camlistore/issues/detail?id=493
Short version: when decoding fails because of e.g a particular tiff tag,
some/most of the rest of the decoded information could still be used.
This change is two-fold:
1) it provides some helper functions to diagnose the error returned by
Decode, and help decide whether the returned decoded information can
stil be used.
2) because sub-IFDs are independent, if parsing one of them fails,
the error is recorded but parsing the others is still attempted.
- Might be caused by an unknown DataType, or a Count of 0, but either
way the tag is invalid
- Check for immediate recursion when decoding IFDs. The 0-length check
above will prevent the obvious case, but it still could be possible to
have a self-referencing directory. This doesn't check for indirect
recursion. Maybe there should be a limit to the number of times
DecodeDir can be called?
- Verify that we error out immediately when an exif tag Count is
MaxUint32, which is likely invalid.
- Verify that we don't try to buffer more than the size of the image
file if the tag has an extraordinarily large Count.
- There seems to be a relatively common corrupt tag which has an offset
of MaxUint32. This is probably not a valid offset, so return early
before we allocate a slice this large.
- Read tag values into a bytes.Buffer. This will prevent allocating
memory beyond the size of the image file in the event that a tag is
invalid or corrupt.
- Fixes issue #20
* renamed TypeCategory to Format and made format calculated upon decoding
rather than repeatedly for every format call
* unexport BadTypecastErr -> wrongFmtErr
* Change String method to just return the string value - and don't have
square brackets if only a single value
* add separate Int and Int64 retrieval methods
* update docs