TokenizeContext processes the input and returns tokens with context support for cancellation. It checks the context at regular intervals (every 100 tokens) to enable fast cancellation. Returns context.Canceled or context.DeadlineExceeded when the context is cancelled. This method is useful for: - L
(ctx context.Context, input []byte)
| 548 | // // Handle timeout |
| 549 | // } |
| 550 | func (t *Tokenizer) TokenizeContext(ctx context.Context, input []byte) ([]models.TokenWithSpan, error) { |
| 551 | // Check context before starting |
| 552 | if err := ctx.Err(); err != nil { |
| 553 | return nil, err |
| 554 | } |
| 555 | |
| 556 | // Record start time for metrics |
| 557 | startTime := time.Now() |
| 558 | |
| 559 | // Validate input size to prevent DoS attacks |
| 560 | if len(input) > MaxInputSize { |
| 561 | err := errors.InputTooLargeError(int64(len(input)), MaxInputSize, models.Location{Line: 1, Column: 0}) |
| 562 | metrics.RecordTokenization(time.Since(startTime), len(input), err) |
| 563 | return nil, err |
| 564 | } |
| 565 | |
| 566 | // Reset state |
| 567 | t.Reset() |
| 568 | t.input = input |
| 569 | |
| 570 | // Pre-allocate line starts slice - reuse if possible |
| 571 | estimatedLines := len(input)/50 + 1 // Estimate 50 chars per line + 1 for initial 0 |
| 572 | if cap(t.lineStarts) < estimatedLines { |
| 573 | t.lineStarts = make([]int, 0, estimatedLines) |
| 574 | } else { |
| 575 | t.lineStarts = t.lineStarts[:0] |
| 576 | } |
| 577 | t.lineStarts = append(t.lineStarts, 0) |
| 578 | |
| 579 | // Pre-scan input to build line start indices |
| 580 | for i := 0; i < len(t.input); i++ { |
| 581 | if t.input[i] == '\n' { |
| 582 | t.lineStarts = append(t.lineStarts, i+1) |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | // Pre-allocate token slice with better capacity estimation |
| 587 | estimatedTokens := len(input) / 4 |
| 588 | if estimatedTokens < 16 { |
| 589 | estimatedTokens = 16 // At least 16 tokens |
| 590 | } |
| 591 | tokens := make([]models.TokenWithSpan, 0, estimatedTokens) |
| 592 | |
| 593 | // Get a buffer from the pool for string operations |
| 594 | buf := getBuffer() |
| 595 | defer putBuffer(buf) |
| 596 | |
| 597 | var tokenErr error |
| 598 | func() { |
| 599 | // Ensure proper cleanup even if we panic |
| 600 | defer func() { |
| 601 | if r := recover(); r != nil { |
| 602 | tokenErr = errors.TokenizerPanicError(r, t.getCurrentPosition()) |
| 603 | } |
| 604 | }() |
| 605 | |
| 606 | for t.pos.Index < len(t.input) { |
| 607 | // Check context every 100 tokens for cancellation |