Skip to content

Conversation

jeckersb
Copy link
Collaborator

@jeckersb jeckersb commented Sep 11, 2025

Add add_or_modify and remove methods to Cmdline in both bytes
and utf8 modules, along with unit tests.

Closes: #1596
Signed-off-by: John Eckersberg jeckersb@redhat.com

@bootc-bot bootc-bot bot requested a review from cgwalters September 11, 2025 20:37
@jeckersb jeckersb force-pushed the cmdline-manipulation branch from 5f83488 to c2228fe Compare September 11, 2025 20:37
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds useful methods (add_or_modify and remove) for manipulating kernel command line parameters in both bytes and utf8 modules, along with corresponding unit tests. The overall approach is sound.

My review includes suggestions to refactor the implementations of add_or_modify and remove in the bytes module. The proposed changes aim to improve code clarity by replacing closures with side effects with more explicit for loops. This also addresses a bug related to handling empty command lines and removes some redundant code. The rest of the changes look good.

@jeckersb jeckersb force-pushed the cmdline-manipulation branch from c2228fe to aca4c99 Compare September 11, 2025 20:46
@jeckersb
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces add_or_modify and remove methods for manipulating kernel command line parameters, along with corresponding unit tests. The changes are a good addition, but I've identified a critical issue in the add_or_modify implementation concerning the handling of duplicate keys, which could lead to incorrect command line generation. I've provided a corrected implementation that resolves this and another minor bug. Additionally, I've pointed out a small redundancy and suggested improvements to test coverage to make the new functionality more robust.

@jeckersb
Copy link
Collaborator Author

interestingly enough i forgot to add my local changes and it generated a wildly different review for the same code 😆

@jeckersb jeckersb force-pushed the cmdline-manipulation branch from aca4c99 to ab3a94f Compare September 11, 2025 20:54
@jeckersb
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds useful methods for manipulating kernel command line parameters. The implementation of add_or_modify has a bug related to handling duplicate keys and another minor issue with appending to an empty command line. I've provided a detailed comment with a suggested fix. The rest of the changes look good.

@jeckersb jeckersb force-pushed the cmdline-manipulation branch 2 times, most recently from 25dffe8 to 872ccdd Compare September 11, 2025 21:12
@jeckersb
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces add_or_modify and remove methods for manipulating kernel command line parameters in both bytes and utf8 modules, complete with comprehensive unit tests. The implementation is robust and handles various edge cases like duplicate parameters and empty command lines. The code quality is high, and the changes are a valuable addition. I have one suggestion to improve the readability of the add_or_modify function's control flow.

@jeckersb jeckersb force-pushed the cmdline-manipulation branch from 872ccdd to 7833932 Compare September 11, 2025 21:20
@jeckersb
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces useful methods for manipulating kernel command line parameters, add_or_modify and remove, for both bytes and utf8 variants. The implementation is generally solid and includes a good set of unit tests to cover various scenarios. I've identified a minor robustness issue when appending new parameters and have also suggested an API improvement to enhance encapsulation and test clarity. Overall, these are great additions to the library.

///
/// Returns `false` if the parameter already existed with the same
/// content.
pub fn add_or_modify(&mut self, param: Parameter) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if for the use cases that require mutation we should just create an ordered hashmap from it?

That's basically how https://github.com/ostreedev/ostree/blob/main/src/libostree/ostree-kernel-args.c works.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this relates to #1608 (comment) in that how duplicate keys should be handled when setting a parameter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah in the current state (as in how this patch is at this very moment...) if you construct a Cmdline with duplicate parameters, we allow that and don't try to do any verification at build time. But if you remove a duplicate, all duplicates are removed. And if you add_or_modify a duplicate, it removes all of the old instances and replaces it with just the single new one. I think that makes sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: "ordered hashmap", we'd have to pull in something like IndexMap? I see ostree-ext already uses that...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep indexmap is very widely used, I think it'd make sense here. Though we do need to preserve multiple keys so we should map from &str -> Vec<&str>

Add `add_or_modify` and `remove` methods to `Cmdline` in both `bytes`
and `utf8` modules, along with unit tests.

Closes: bootc-dev#1596
Signed-off-by: John Eckersberg <jeckersb@redhat.com>
@jeckersb jeckersb force-pushed the cmdline-manipulation branch from 7833932 to 0555eac Compare September 11, 2025 21:34
@jeckersb
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces add_or_modify and remove methods for manipulating kernel command line parameters, along with corresponding unit tests. The changes are well-implemented and the new functionality is useful. The utf8 module correctly delegates to the bytes module, promoting code reuse. The tests are comprehensive and cover various edge cases. I have one suggestion to improve the performance and clarity of the add_or_modify method for the case where a new parameter is being added.

Comment on lines +138 to +179
pub fn add_or_modify(&mut self, param: Parameter) -> bool {
let mut new_params = Vec::new();
let mut modified = false;
let mut seen_key = false;

for p in self.iter() {
if p.key == param.key {
if !seen_key {
// This is the first time we've seen this key.
// We will replace it with the new parameter.
if p != param {
modified = true;
}
new_params.push(param.parameter);
} else {
// This is a subsequent parameter with the same key.
// We will remove it, which constitutes a modification.
modified = true;
}
seen_key = true;
} else {
new_params.push(p.parameter);
}
}

if !seen_key {
// The parameter was not found, so we append it.
let self_mut = self.0.to_mut();
if !self_mut.is_empty() && !self_mut.last().unwrap().is_ascii_whitespace() {
self_mut.push(b' ');
}
self_mut.extend_from_slice(param.parameter);
return true;
}
if modified {
self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
true
} else {
// The parameter already existed with the same content, and there were no duplicates.
false
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of add_or_modify is a single-pass algorithm, which is efficient for modifications. However, when adding a new parameter (i.e., the key does not already exist), the implementation iterates through all existing parameters and builds a new_params vector, which is then discarded. This results in unnecessary allocation and processing.

A more optimal approach for the 'add' case would be to first check if the key exists. If it doesn't, you can take a fast path to append the new parameter without building the intermediate new_params vector. This change would improve performance for additions and make the code's intent clearer by separating the 'add' and 'modify' logic paths.

Here is a suggested refactoring that implements this approach:

    pub fn add_or_modify(&mut self, param: Parameter) -> bool {
        // By checking for existence first, we can fast-path the common case of adding a new parameter.
        if !self.iter().any(|p| p.key == param.key) {
            // The parameter was not found, so we append it.
            let self_mut = self.0.to_mut();
            if !self_mut.is_empty() && !self_mut.last().unwrap().is_ascii_whitespace() {
                self_mut.push(b' ');
            }
            self_mut.extend_from_slice(param.parameter);
            return true;
        }

        // The key exists, so we need to rebuild.
        let mut new_params = Vec::new();
        let mut modified = false;
        let mut seen_key = false;

        for p in self.iter() {
            if p.key == param.key {
                if !seen_key {
                    // This is the first time we've seen this key.
                    // We will replace it with the new parameter.
                    if p != param {
                        modified = true;
                    }
                    new_params.push(param.parameter);
                } else {
                    // This is a subsequent parameter with the same key.
                    // We will remove it, which constitutes a modification.
                    modified = true;
                }
                seen_key = true;
            } else {
                new_params.push(p.parameter);
            }
        }

        if modified {
            self.0 = Cow::Owned(new_params.join(b" ".as_slice()));
            true
        } else {
            // The parameter already existed with the same content, and there were no duplicates.
            false
        }
    }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, constructing the vec isn't that expensive, it's just copying the slice references. I purposefully tried to avoid iterating the whole thing twice, but kinda indifferent on this one.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we shouldn't care about performance here, only correctness and maintainability

@cgwalters cgwalters merged commit 68c4e4d into bootc-dev:main Sep 12, 2025
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kernel_cmdline should support manipulating parameters

2 participants