-
Notifications
You must be signed in to change notification settings - Fork 3
Pre-process iCalendars line by line to avoid OOM on large files #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
af8f8f6
Fix OOM issues
ArnyminerZ 9692929
Remove reset
ArnyminerZ 84e33f3
Get rid of Android's Log
ArnyminerZ fd65367
Fix function calling
ArnyminerZ dad980b
Ignore exception in try-catch
ArnyminerZ 6f52535
Fix tests
ArnyminerZ beaf42b
Merge branch 'main' into 90-oom-exception-with-large-icalendar-files
ArnyminerZ 73380e7
Get rid of `regexpForProblem` in `StreamPreprocessor`
ArnyminerZ 0da87e2
Improve KDoc
ArnyminerZ 4ef91b5
Rename argument
ArnyminerZ bb47bc0
Move warning
ArnyminerZ 1b17033
Closing readers
ArnyminerZ fca4cc1
Make it clear why we use LF
ArnyminerZ 20483d9
Use Guava's `CharSource` and get rid of `SequenceReader`
ArnyminerZ 6652463
Optimize imports
ArnyminerZ 3910eef
Disable inspection
ArnyminerZ a257c2c
Added Spotbugs annotations
ArnyminerZ 7948036
Add annotations
ArnyminerZ 0bda829
Merge branch 'main' into 90-oom-exception-with-large-icalendar-files
rfc2822 43cbcc3
[WIP] Process line by line
rfc2822 1f7298a
KDoc
rfc2822 17ca072
Move ICalPreprocessorInstrumentedTest to unit tests
rfc2822 cc53c37
KDoc
rfc2822 68187c4
Add test
rfc2822 08a1d6b
Make some fields internal
rfc2822 c1acb29
KDoc
rfc2822 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
94 changes: 94 additions & 0 deletions
94
...Test/kotlin/at/bitfire/synctools/icalendar/validation/ICalPreprocessorInstrumentedTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| /* | ||
| * This file is part of bitfireAT/synctools which is released under GPLv3. | ||
| * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. | ||
| * SPDX-License-Identifier: GPL-3.0-or-later | ||
| */ | ||
|
|
||
| package at.bitfire.synctools.icalendar.validation | ||
|
|
||
| import org.junit.Test | ||
| import java.io.Reader | ||
| import java.util.UUID | ||
|
|
||
| class ICalPreprocessorInstrumentedTest { | ||
|
|
||
| class VCalendarReaderGenerator(val eventCount: Int = Int.MAX_VALUE) : Reader() { | ||
ArnyminerZ marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private var stage = 0 // 0 = header, 1 = events, 2 = footer, 3 = done | ||
| private var eventIdx = 0 | ||
| private var current: String? = null | ||
| private var pos = 0 | ||
|
|
||
| override fun reset() { | ||
| stage = 0 | ||
| eventIdx = 0 | ||
| current = null | ||
| pos = 0 | ||
| } | ||
|
|
||
| override fun read(cbuf: CharArray, off: Int, len: Int): Int { | ||
| var charsRead = 0 | ||
| while (charsRead < len) { | ||
| if (current == null || pos >= current!!.length) { | ||
| current = when (stage) { | ||
| 0 -> { | ||
| stage = 1 | ||
| """ | ||
| BEGIN:VCALENDAR | ||
| PRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN | ||
| VERSION:2.0 | ||
| """.trimIndent() + "\n" | ||
| } | ||
| 1 -> { | ||
| if (eventIdx < eventCount) { | ||
| val event = """ | ||
| BEGIN:VEVENT | ||
| DTSTAMP:19960704T120000Z | ||
| UID:${UUID.randomUUID()} | ||
| ORGANIZER:mailto:jsmith@example.com | ||
| DTSTART:19960918T143000Z | ||
| DTEND:19960920T220000Z | ||
| STATUS:CONFIRMED | ||
| CATEGORIES:CONFERENCE | ||
| SUMMARY:Event $eventIdx | ||
| DESCRIPTION:Event $eventIdx description | ||
| END:VEVENT | ||
| """.trimIndent() + "\n" | ||
| eventIdx++ | ||
| event | ||
| } else { | ||
| stage = 2 | ||
| null | ||
| } | ||
| } | ||
| 2 -> { | ||
| stage = 3 | ||
| "END:VCALENDAR\n" | ||
| } | ||
| else -> return if (charsRead == 0) -1 else charsRead | ||
| } | ||
| pos = 0 | ||
| if (current == null) continue // move to next stage | ||
| } | ||
| val charsLeft = current!!.length - pos | ||
| val toRead = minOf(len - charsRead, charsLeft) | ||
| current!!.toCharArray(pos, pos + toRead).copyInto(cbuf, off + charsRead) | ||
| pos += toRead | ||
| charsRead += toRead | ||
| } | ||
| return charsRead | ||
| } | ||
|
|
||
| override fun close() { | ||
| // No resources to release | ||
| current = null | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun testParse_SuperLargeFiles() { | ||
rfc2822 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| val preprocessor = ICalPreprocessor() | ||
| val reader = VCalendarReaderGenerator() | ||
| preprocessor.preprocessStream(reader) | ||
| // no exception called | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
lib/src/main/kotlin/at/bitfire/synctools/utils/SequenceReader.kt
ArnyminerZ marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| * This file is part of bitfireAT/synctools which is released under GPLv3. | ||
| * Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details. | ||
| * SPDX-License-Identifier: GPL-3.0-or-later | ||
| */ | ||
|
|
||
| package at.bitfire.synctools.utils | ||
|
|
||
| import java.io.Reader | ||
|
|
||
| /** | ||
| * A [Reader] that allows loading data from a [Sequence] of [String]s. | ||
| */ | ||
| class SequenceReader(lines: Sequence<String>) : Reader() { | ||
| private var iterator = lines.iterator() | ||
| private var currentLine: String? = null | ||
| private var currentPos = 0 | ||
| private var closed = false | ||
|
|
||
| override fun read(cbuf: CharArray, off: Int, len: Int): Int { | ||
| check(!closed) { "Reader closed" } | ||
| var charsRead = 0 | ||
| while (charsRead < len) { | ||
| if (currentLine == null || currentPos >= currentLine!!.length) { | ||
| if (!iterator.hasNext()) { | ||
| if (charsRead == 0) return -1 | ||
| break | ||
| } | ||
| currentLine = iterator.next() + "\n" | ||
| currentPos = 0 | ||
| } | ||
| val charsToCopy = minOf(len - charsRead, currentLine!!.length - currentPos) | ||
| currentLine!!.toCharArray(currentPos, currentPos + charsToCopy).copyInto(cbuf, off + charsRead) | ||
ArnyminerZ marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| currentPos += charsToCopy | ||
| charsRead += charsToCopy | ||
| } | ||
| return charsRead | ||
| } | ||
|
|
||
| override fun close() { | ||
| closed = true | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.