How ResetInput and ResetOutput Control Instance State in GeoIP Operations

ResetInput and ResetOutput reinitialize the input and output converter slices in the Instance struct to empty slices, enabling safe reuse of the data container across complex workflows like REPL mode without reloading source files.

The loyalsoldier/geoip library uses an Instance struct as the central orchestrator for geolocation lookups. Understanding how ResetInput and ResetOutput modify the internal state is essential for optimizing both single-run CLI commands and interactive sessions where the same dataset is queried repeatedly.

The Instance Architecture and Converter Slices

At the core of the library, the Instance maintains two ordered slices that manage data flow:

  • input []InputConverter – Converters that read source data and populate a container
  • output []OutputConverter – Converters that consume the container and generate results

According to the source code in lib/instance.go, the private instance struct is defined as:

type instance struct {
    input  []InputConverter
    output []OutputConverter
}

These slices hold the registered converters that execute during the Run() pipeline.

How ResetInput and ResetOutput Modify Instance State

Both methods simply reinitialize their respective slices to empty slices, effectively unregistering all previously added converters.

ResetInput Implementation

Located at lines 81–83 in lib/instance.go, ResetInput clears all input converters:

func (i *instance) ResetInput() {
    i.input = make([]InputConverter, 0)
}

This removes all InputConverter registrations, allowing fresh input sources to be configured.

ResetOutput Implementation

Located at lines 85–87 in the same file, ResetOutput clears all output converters:

func (i *instance) ResetOutput() {
    i.output = make([]OutputConverter, 0)
}

This is particularly critical in REPL workflows where output logic changes per query while the input data remains constant.

Impact on Workflow Patterns

The state impact varies significantly between single-run operations and interactive REPL modes.

Single-Run Operations (No Reset Required)

For one-off commands like geoip lookup <ip>, the workflow creates a fresh Instance:

  1. Call lib.NewInstance() (slices are initialized empty)
  2. Register one input converter via AddInput()
  3. Register one output converter via AddOutput()
  4. Execute Run(), which calls RunInput() then RunOutput()

Since the instance is newly created, its slices are already empty, making reset calls unnecessary.

REPL Mode (ResetOutput Required)

In interactive lookup mode, the same Instance and container are reused across multiple searches. As implemented in lookup.go (lines 127–129), the pattern is:

instance, _ := lib.NewInstance()
instance.AddInput(getInputForLookup(...))   // Register input once

container := lib.NewContainer()
instance.RunInput(container)              // Populate container once

for each search {
    instance.ResetOutput()                 // Clear previous output logic
    instance.AddOutput(getOutputForLookup(search, ...))
    instance.RunOutput(container)          // Reuse existing container
}

State impact during REPL:

  • The input slice remains constant (holding the original file reader)
  • The output slice is cleared and repopulated for each iteration
  • The container retains parsed data, avoiding expensive reloads

Switching Data Sources (Both Resets Required)

When changing input files or formats entirely, both slices must be cleared:

inst.ResetInput()
inst.ResetOutput()
inst.AddInput(newSource)
inst.AddOutput(newFormat)
inst.Run()

This pattern discards both the old input pipeline and any accumulated output configurations.

Practical Implementation Examples

Example 1: Simple One-Off Lookup

For single IP lookups where the instance is discarded afterward, no reset calls are needed:

inst, _ := lib.NewInstance()
inst.AddInput(getInputForLookup("text", "true", uri, ""))   // Load list file
inst.AddOutput(getOutputForLookup("1.2.3.4"))             // Search IP
inst.Run() // Executes input → output pipeline once

Example 2: Interactive REPL with ResetOutput

This pattern from lookup.go demonstrates efficient reuse of the loaded dataset:

inst, _ := lib.NewInstance()
inst.AddInput(getInputForLookup("text", "true", uri, ""))

c := lib.NewContainer()
inst.RunInput(c) // Load the list only once

for {
    fmt.Print("Enter IP (or \"exit\"): ")
    var ip string
    fmt.Scanln(&ip)
    if ip == "exit" { break }

    // Clear previous output logic, then set fresh one for new query
    inst.ResetOutput()
    inst.AddOutput(getOutputForLookup(ip))
    inst.RunOutput(c) // Reuse the same container
}

Example 3: Switching Input and Output Sources

When migrating between different file formats or databases, reset both slices:

inst, _ := lib.NewInstance()

// First operation using CSV source
inst.AddInput(csvInput)
inst.AddOutput(csvOutput)
inst.Run()

// Switch to MMDB source
inst.ResetInput()
inst.ResetOutput()
inst.AddInput(mmdbInput)
inst.AddOutput(mmdbOutput)
inst.Run()

Summary

  • ResetInput (lines 81–83 in lib/instance.go) reinitializes the input slice to empty, removing all InputConverter registrations
  • ResetOutput (lines 85–87 in lib/instance.go) reinitializes the output slice to empty, removing all OutputConverter registrations
  • In REPL workflows (lookup.go lines 127–129), ResetOutput enables query-specific output logic while preserving the loaded container data
  • Single-run operations do not require resets because NewInstance() initializes empty slices
  • Resetting both slices allows complete reconfiguration of the Instance for different data sources or output formats

Frequently Asked Questions

What is the difference between ResetInput and ResetOutput?

ResetInput clears the slice holding InputConverter implementations that read source data into the container, while ResetOutput clears the slice holding OutputConverter implementations that format and return results. According to lib/instance.go, both simply reinitialize their respective slices to empty arrays using make([]Type, 0).

When should I use ResetOutput instead of creating a new Instance?

Use ResetOutput in interactive or batch scenarios where the input dataset remains constant but output requirements change per query. As shown in lookup.go, this avoids the expensive overhead of reloading large IP lists into the container via RunInput() for every operation.

Does ResetInput clear the container data?

No. ResetInput only unregisters the converter functions; it does not modify the container that stores parsed data. The container is passed explicitly to RunInput() and RunOutput() and persists independently until garbage collected or explicitly overwritten.

How do these methods affect performance in REPL mode?

ResetOutput significantly improves REPL performance by allowing the same Container instance—populated once via RunInput()—to be reused across hundreds of lookups. Without this method, you would need to either create new Instance objects (wasting memory) or accumulate output converters (causing logical errors), as the source code in lookup.go demonstrates by calling ResetOutput() at the start of each loop iteration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →