[libcamera-devel,v4,2/4] libcamera: V4L2Device: Use std::vector for v4l2_ext_control in getControls()
diff mbox series

Message ID 20210422021809.520675-3-hiroh@chromium.org
State Superseded
Headers show
Series
  • Clean up V4L2Device code
Related show

Commit Message

Hirokazu Honda April 22, 2021, 2:18 a.m. UTC
The original code uses Variable-Length-Array, which is not
officially supported in C++. This replaces the array with
std::vector.

Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
---
 src/libcamera/v4l2_device.cpp | 31 ++++++++++++++-----------------
 1 file changed, 14 insertions(+), 17 deletions(-)

Comments

Jacopo Mondi April 22, 2021, 7:50 a.m. UTC | #1
Hi Hiro,

Interesting https://stackoverflow.com/a/21519063

I initially thougth replacing VLA in C++ code was mostly a matter of
style...

On Thu, Apr 22, 2021 at 11:18:07AM +0900, Hirokazu Honda wrote:
> The original code uses Variable-Length-Array, which is not
> officially supported in C++. This replaces the array with
> std::vector.

One thing that always bothered me of this class is

	std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_;
	std::vector<std::unique_ptr<V4L2ControlId>> controlIds_;

everytime I see these identifiers I forget they refer to v4l2 stuff.
Should they be renamed ?

>
> Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> ---
>  src/libcamera/v4l2_device.cpp | 31 ++++++++++++++-----------------
>  1 file changed, 14 insertions(+), 17 deletions(-)
>
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index 3c32d682..617e6ec9 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -173,8 +173,7 @@ void V4L2Device::close()
>   */
>  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
>  {
> -	unsigned int count = ids.size();
> -	if (count == 0)
> +	if (ids.empty())
>  		return {};
>
>  	ControlList ctrls{ controls_ };
> @@ -190,22 +189,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
>  		ctrls.set(id, {});
>  	}
>
> -	struct v4l2_ext_control v4l2Ctrls[count];
> -	memset(v4l2Ctrls, 0, sizeof(v4l2Ctrls));
> +	std::vector<v4l2_ext_control> v4l2Ctrls(ctrls.size());
> +	memset(v4l2Ctrls.data(), 0, sizeof(v4l2_ext_control) * ctrls.size());

Do you need the memset ?

I read:

        explicit vector( size_type count );

        4) Constructs the container with count default-inserted instances of T. No copies are made.

"default inserted instances of T" makes me wonder if a
default-inserted v4l2_ext_control is actually zeroed

I wonder if using reserve() would be better

>
> -	unsigned int i = 0;
> -	for (auto &ctrl : ctrls) {
> -		unsigned int id = ctrl.first;
> +	for (auto [ctrl, i] = std::pair(ctrls.begin(), 0u); i < ctrls.size(); ctrl++, i++) {

C++ is the language of the beast.
Neat though

> +		const unsigned int id = ctrl->first;
>  		const struct v4l2_query_ext_ctrl &info = controlInfo_[id];
> +		v4l2_ext_control &v4l2Ctrl = v4l2Ctrls[i];
>
>  		if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
>  			ControlType type;
> -
>  			switch (info.type) {
>  			case V4L2_CTRL_TYPE_U8:
>  				type = ControlTypeByte;
>  				break;
> -
>  			default:
>  				LOG(V4L2, Error)
>  					<< "Unsupported payload control type "
> @@ -213,7 +210,7 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
>  				return {};
>  			}
>
> -			ControlValue &value = ctrl.second;
> +			ControlValue &value = ctrl->second;
>  			value.reserve(type, true, info.elems);
>  			Span<uint8_t> data = value.data();
>
> @@ -221,21 +218,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
>  			v4l2Ctrls[i].size = data.size();
>  		}
>
> -		v4l2Ctrls[i].id = id;
> -		i++;
> +		v4l2Ctrl.id = id;

However, shouldn't we try to squeeze the two loops (the one that
initialize ctrls and this one) in a single one now that the ordering
restrictions is gone ?

>  	}
>
>  	struct v4l2_ext_controls v4l2ExtCtrls = {};
>  	v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL;
> -	v4l2ExtCtrls.controls = v4l2Ctrls;
> -	v4l2ExtCtrls.count = count;
> +	v4l2ExtCtrls.controls = v4l2Ctrls.data();
> +	v4l2ExtCtrls.count = v4l2Ctrls.size();
>
>  	int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls);
>  	if (ret) {
>  		unsigned int errorIdx = v4l2ExtCtrls.error_idx;
>
>  		/* Generic validation error. */
> -		if (errorIdx == 0 || errorIdx >= count) {
> +		if (errorIdx == 0 || errorIdx >= v4l2Ctrls.size()) {
>  			LOG(V4L2, Error) << "Unable to read controls: "
>  					 << strerror(-ret);
>  			return {};
> @@ -244,10 +240,11 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
>  		/* A specific control failed. */
>  		LOG(V4L2, Error) << "Unable to read control " << errorIdx
>  				 << ": " << strerror(-ret);
> -		count = errorIdx - 1;
> +
> +		v4l2Ctrls.resize(errorIdx);
>  	}
>
> -	updateControls(&ctrls, v4l2Ctrls, count);
> +	updateControls(&ctrls, v4l2Ctrls.data(), v4l2Ctrls.size());
>
>  	return ctrls;
>  }
> --
> 2.31.1.368.gbe11c130af-goog
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart April 23, 2021, 3:05 a.m. UTC | #2
On Thu, Apr 22, 2021 at 09:50:46AM +0200, Jacopo Mondi wrote:
> Hi Hiro,
> 
> Interesting https://stackoverflow.com/a/21519063

"An aesthetic or correct way of invoking an action that requires DOM
manipulation as well as controller related tasks in EmberJS"

Please don't tell me you plan to rewrite libcamera in JS :-)

> I initially thougth replacing VLA in C++ code was mostly a matter of
> style...
> 
> On Thu, Apr 22, 2021 at 11:18:07AM +0900, Hirokazu Honda wrote:
> > The original code uses Variable-Length-Array, which is not
> > officially supported in C++. This replaces the array with
> > std::vector.
> 
> One thing that always bothered me of this class is
> 
> 	std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_;
> 	std::vector<std::unique_ptr<V4L2ControlId>> controlIds_;
> 
> everytime I see these identifiers I forget they refer to v4l2 stuff.
> Should they be renamed ?
> 
> > Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> > ---
> >  src/libcamera/v4l2_device.cpp | 31 ++++++++++++++-----------------
> >  1 file changed, 14 insertions(+), 17 deletions(-)
> >
> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > index 3c32d682..617e6ec9 100644
> > --- a/src/libcamera/v4l2_device.cpp
> > +++ b/src/libcamera/v4l2_device.cpp
> > @@ -173,8 +173,7 @@ void V4L2Device::close()
> >   */
> >  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> >  {
> > -	unsigned int count = ids.size();
> > -	if (count == 0)
> > +	if (ids.empty())
> >  		return {};
> >
> >  	ControlList ctrls{ controls_ };
> > @@ -190,22 +189,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> >  		ctrls.set(id, {});
> >  	}
> >
> > -	struct v4l2_ext_control v4l2Ctrls[count];
> > -	memset(v4l2Ctrls, 0, sizeof(v4l2Ctrls));
> > +	std::vector<v4l2_ext_control> v4l2Ctrls(ctrls.size());
> > +	memset(v4l2Ctrls.data(), 0, sizeof(v4l2_ext_control) * ctrls.size());
> 
> Do you need the memset ?
> 
> I read:
> 
>         explicit vector( size_type count );
> 
>         4) Constructs the container with count default-inserted instances of T. No copies are made.
> 
> "default inserted instances of T" makes me wonder if a
> default-inserted v4l2_ext_control is actually zeroed
> 
> I wonder if using reserve() would be better

In a previous reply, Hiro mentioned that the zero-initialization will
only initialize the first member of unions. memset() is possibly safer.

> >
> > -	unsigned int i = 0;
> > -	for (auto &ctrl : ctrls) {
> > -		unsigned int id = ctrl.first;
> > +	for (auto [ctrl, i] = std::pair(ctrls.begin(), 0u); i < ctrls.size(); ctrl++, i++) {
> 
> C++ is the language of the beast.
> Neat though

I've posted "[PATCH/RFC 1/3] libcamera: utils: Add enumerate view for
range-based for loops" which would allow writing this as

	for (auto &[i, ctrl] = utils::enumerate(ctrls)) {

It's not a blocker though, we can merge this series and rework it later.
I'd however appreciate feedback on the above patch (and the rest of the
series).

> > +		const unsigned int id = ctrl->first;
> >  		const struct v4l2_query_ext_ctrl &info = controlInfo_[id];
> > +		v4l2_ext_control &v4l2Ctrl = v4l2Ctrls[i];
> >
> >  		if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
> >  			ControlType type;
> > -
> >  			switch (info.type) {
> >  			case V4L2_CTRL_TYPE_U8:
> >  				type = ControlTypeByte;
> >  				break;
> > -

I really think those blank lines help readability.

> >  			default:
> >  				LOG(V4L2, Error)
> >  					<< "Unsupported payload control type "
> > @@ -213,7 +210,7 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> >  				return {};
> >  			}
> >
> > -			ControlValue &value = ctrl.second;
> > +			ControlValue &value = ctrl->second;
> >  			value.reserve(type, true, info.elems);
> >  			Span<uint8_t> data = value.data();
> >
> > @@ -221,21 +218,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> >  			v4l2Ctrls[i].size = data.size();
> >  		}
> >
> > -		v4l2Ctrls[i].id = id;
> > -		i++;
> > +		v4l2Ctrl.id = id;
> 
> However, shouldn't we try to squeeze the two loops (the one that
> initialize ctrls and this one) in a single one now that the ordering
> restrictions is gone ?

That's what I replied to 1/4 :-) I'd have a small preference for doing
it in 1/4 as that's where the restriction is removed, or at least in a
patch inserted before 1/4 and 2/4.

> >  	}
> >
> >  	struct v4l2_ext_controls v4l2ExtCtrls = {};
> >  	v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL;
> > -	v4l2ExtCtrls.controls = v4l2Ctrls;
> > -	v4l2ExtCtrls.count = count;
> > +	v4l2ExtCtrls.controls = v4l2Ctrls.data();
> > +	v4l2ExtCtrls.count = v4l2Ctrls.size();
> >
> >  	int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls);
> >  	if (ret) {
> >  		unsigned int errorIdx = v4l2ExtCtrls.error_idx;
> >
> >  		/* Generic validation error. */
> > -		if (errorIdx == 0 || errorIdx >= count) {
> > +		if (errorIdx == 0 || errorIdx >= v4l2Ctrls.size()) {
> >  			LOG(V4L2, Error) << "Unable to read controls: "
> >  					 << strerror(-ret);
> >  			return {};
> > @@ -244,10 +240,11 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> >  		/* A specific control failed. */
> >  		LOG(V4L2, Error) << "Unable to read control " << errorIdx
> >  				 << ": " << strerror(-ret);
> > -		count = errorIdx - 1;
> > +
> > +		v4l2Ctrls.resize(errorIdx);
> >  	}
> >
> > -	updateControls(&ctrls, v4l2Ctrls, count);
> > +	updateControls(&ctrls, v4l2Ctrls.data(), v4l2Ctrls.size());
> >
> >  	return ctrls;
> >  }
Hirokazu Honda April 23, 2021, 4:38 a.m. UTC | #3
On Fri, Apr 23, 2021 at 12:05 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> On Thu, Apr 22, 2021 at 09:50:46AM +0200, Jacopo Mondi wrote:
> > Hi Hiro,
> >
> > Interesting https://stackoverflow.com/a/21519063
>
> "An aesthetic or correct way of invoking an action that requires DOM
> manipulation as well as controller related tasks in EmberJS"
>
> Please don't tell me you plan to rewrite libcamera in JS :-)
>
> > I initially thougth replacing VLA in C++ code was mostly a matter of
> > style...
> >
> > On Thu, Apr 22, 2021 at 11:18:07AM +0900, Hirokazu Honda wrote:
> > > The original code uses Variable-Length-Array, which is not
> > > officially supported in C++. This replaces the array with
> > > std::vector.
> >
> > One thing that always bothered me of this class is
> >
> >       std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_;
> >       std::vector<std::unique_ptr<V4L2ControlId>> controlIds_;
> >
> > everytime I see these identifiers I forget they refer to v4l2 stuff.
> > Should they be renamed ?
> >
> > > Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> > > ---
> > >  src/libcamera/v4l2_device.cpp | 31 ++++++++++++++-----------------
> > >  1 file changed, 14 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > > index 3c32d682..617e6ec9 100644
> > > --- a/src/libcamera/v4l2_device.cpp
> > > +++ b/src/libcamera/v4l2_device.cpp
> > > @@ -173,8 +173,7 @@ void V4L2Device::close()
> > >   */
> > >  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  {
> > > -   unsigned int count = ids.size();
> > > -   if (count == 0)
> > > +   if (ids.empty())
> > >             return {};
> > >
> > >     ControlList ctrls{ controls_ };
> > > @@ -190,22 +189,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >             ctrls.set(id, {});
> > >     }
> > >
> > > -   struct v4l2_ext_control v4l2Ctrls[count];
> > > -   memset(v4l2Ctrls, 0, sizeof(v4l2Ctrls));
> > > +   std::vector<v4l2_ext_control> v4l2Ctrls(ctrls.size());
> > > +   memset(v4l2Ctrls.data(), 0, sizeof(v4l2_ext_control) * ctrls.size());
> >
> > Do you need the memset ?
> >
> > I read:
> >
> >         explicit vector( size_type count );
> >
> >         4) Constructs the container with count default-inserted instances of T. No copies are made.
> >
> > "default inserted instances of T" makes me wonder if a
> > default-inserted v4l2_ext_control is actually zeroed
> >
> > I wonder if using reserve() would be better
>
> In a previous reply, Hiro mentioned that the zero-initialization will
> only initialize the first member of unions. memset() is possibly safer.
>
> > >
> > > -   unsigned int i = 0;
> > > -   for (auto &ctrl : ctrls) {
> > > -           unsigned int id = ctrl.first;
> > > +   for (auto [ctrl, i] = std::pair(ctrls.begin(), 0u); i < ctrls.size(); ctrl++, i++) {
> >
> > C++ is the language of the beast.
> > Neat though
>
> I've posted "[PATCH/RFC 1/3] libcamera: utils: Add enumerate view for
> range-based for loops" which would allow writing this as
>
>         for (auto &[i, ctrl] = utils::enumerate(ctrls)) {
>
> It's not a blocker though, we can merge this series and rework it later.
> I'd however appreciate feedback on the above patch (and the rest of the
> series).

Thanks for informing me.
I would love zip and enumerator iterators. :)
I will review them later.

>
> > > +           const unsigned int id = ctrl->first;
> > >             const struct v4l2_query_ext_ctrl &info = controlInfo_[id];
> > > +           v4l2_ext_control &v4l2Ctrl = v4l2Ctrls[i];
> > >
> > >             if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
> > >                     ControlType type;
> > > -
> > >                     switch (info.type) {
> > >                     case V4L2_CTRL_TYPE_U8:
> > >                             type = ControlTypeByte;
> > >                             break;
> > > -
>
> I really think those blank lines help readability.
>
> > >                     default:
> > >                             LOG(V4L2, Error)
> > >                                     << "Unsupported payload control type "
> > > @@ -213,7 +210,7 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >                             return {};
> > >                     }
> > >
> > > -                   ControlValue &value = ctrl.second;
> > > +                   ControlValue &value = ctrl->second;
> > >                     value.reserve(type, true, info.elems);
> > >                     Span<uint8_t> data = value.data();
> > >
> > > @@ -221,21 +218,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >                     v4l2Ctrls[i].size = data.size();
> > >             }
> > >
> > > -           v4l2Ctrls[i].id = id;
> > > -           i++;
> > > +           v4l2Ctrl.id = id;
> >
> > However, shouldn't we try to squeeze the two loops (the one that
> > initialize ctrls and this one) in a single one now that the ordering
> > restrictions is gone ?
>
> That's what I replied to 1/4 :-) I'd have a small preference for doing
> it in 1/4 as that's where the restriction is removed, or at least in a
> patch inserted before 1/4 and 2/4.
>
> > >     }
> > >
> > >     struct v4l2_ext_controls v4l2ExtCtrls = {};
> > >     v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL;
> > > -   v4l2ExtCtrls.controls = v4l2Ctrls;
> > > -   v4l2ExtCtrls.count = count;
> > > +   v4l2ExtCtrls.controls = v4l2Ctrls.data();
> > > +   v4l2ExtCtrls.count = v4l2Ctrls.size();
> > >
> > >     int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls);
> > >     if (ret) {
> > >             unsigned int errorIdx = v4l2ExtCtrls.error_idx;
> > >
> > >             /* Generic validation error. */
> > > -           if (errorIdx == 0 || errorIdx >= count) {
> > > +           if (errorIdx == 0 || errorIdx >= v4l2Ctrls.size()) {
> > >                     LOG(V4L2, Error) << "Unable to read controls: "
> > >                                      << strerror(-ret);
> > >                     return {};
> > > @@ -244,10 +240,11 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >             /* A specific control failed. */
> > >             LOG(V4L2, Error) << "Unable to read control " << errorIdx
> > >                              << ": " << strerror(-ret);
> > > -           count = errorIdx - 1;
> > > +
> > > +           v4l2Ctrls.resize(errorIdx);
> > >     }
> > >
> > > -   updateControls(&ctrls, v4l2Ctrls, count);
> > > +   updateControls(&ctrls, v4l2Ctrls.data(), v4l2Ctrls.size());
> > >
> > >     return ctrls;
> > >  }
>
> --
> Regards,
>
> Laurent Pinchart
Jacopo Mondi April 23, 2021, 7:03 a.m. UTC | #4
On Fri, Apr 23, 2021 at 06:05:50AM +0300, Laurent Pinchart wrote:
> On Thu, Apr 22, 2021 at 09:50:46AM +0200, Jacopo Mondi wrote:
> > Hi Hiro,
> >
> > Interesting https://stackoverflow.com/a/21519063
>
> "An aesthetic or correct way of invoking an action that requires DOM
> manipulation as well as controller related tasks in EmberJS"

What the heck! I don't even know what EmberJS is!  I clicked on the
'share' button of stackoverflow post about VLA in C++ and that's the
link I get. Didn't know "share" actually meant "share a random
stackoverflow thread"  :)

>
> Please don't tell me you plan to rewrite libcamera in JS :-)
>
> > I initially thougth replacing VLA in C++ code was mostly a matter of
> > style...
> >
> > On Thu, Apr 22, 2021 at 11:18:07AM +0900, Hirokazu Honda wrote:
> > > The original code uses Variable-Length-Array, which is not
> > > officially supported in C++. This replaces the array with
> > > std::vector.
> >
> > One thing that always bothered me of this class is
> >
> > 	std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_;
> > 	std::vector<std::unique_ptr<V4L2ControlId>> controlIds_;
> >
> > everytime I see these identifiers I forget they refer to v4l2 stuff.
> > Should they be renamed ?
> >
> > > Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> > > ---
> > >  src/libcamera/v4l2_device.cpp | 31 ++++++++++++++-----------------
> > >  1 file changed, 14 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > > index 3c32d682..617e6ec9 100644
> > > --- a/src/libcamera/v4l2_device.cpp
> > > +++ b/src/libcamera/v4l2_device.cpp
> > > @@ -173,8 +173,7 @@ void V4L2Device::close()
> > >   */
> > >  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  {
> > > -	unsigned int count = ids.size();
> > > -	if (count == 0)
> > > +	if (ids.empty())
> > >  		return {};
> > >
> > >  	ControlList ctrls{ controls_ };
> > > @@ -190,22 +189,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  		ctrls.set(id, {});
> > >  	}
> > >
> > > -	struct v4l2_ext_control v4l2Ctrls[count];
> > > -	memset(v4l2Ctrls, 0, sizeof(v4l2Ctrls));
> > > +	std::vector<v4l2_ext_control> v4l2Ctrls(ctrls.size());
> > > +	memset(v4l2Ctrls.data(), 0, sizeof(v4l2_ext_control) * ctrls.size());
> >
> > Do you need the memset ?
> >
> > I read:
> >
> >         explicit vector( size_type count );
> >
> >         4) Constructs the container with count default-inserted instances of T. No copies are made.
> >
> > "default inserted instances of T" makes me wonder if a
> > default-inserted v4l2_ext_control is actually zeroed
> >
> > I wonder if using reserve() would be better
>
> In a previous reply, Hiro mentioned that the zero-initialization will
> only initialize the first member of unions. memset() is possibly safer.
>
> > >
> > > -	unsigned int i = 0;
> > > -	for (auto &ctrl : ctrls) {
> > > -		unsigned int id = ctrl.first;
> > > +	for (auto [ctrl, i] = std::pair(ctrls.begin(), 0u); i < ctrls.size(); ctrl++, i++) {
> >
> > C++ is the language of the beast.
> > Neat though
>
> I've posted "[PATCH/RFC 1/3] libcamera: utils: Add enumerate view for
> range-based for loops" which would allow writing this as
>
> 	for (auto &[i, ctrl] = utils::enumerate(ctrls)) {
>
> It's not a blocker though, we can merge this series and rework it later.
> I'd however appreciate feedback on the above patch (and the rest of the
> series).
>
> > > +		const unsigned int id = ctrl->first;
> > >  		const struct v4l2_query_ext_ctrl &info = controlInfo_[id];
> > > +		v4l2_ext_control &v4l2Ctrl = v4l2Ctrls[i];
> > >
> > >  		if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
> > >  			ControlType type;
> > > -
> > >  			switch (info.type) {
> > >  			case V4L2_CTRL_TYPE_U8:
> > >  				type = ControlTypeByte;
> > >  				break;
> > > -
>
> I really think those blank lines help readability.
>
> > >  			default:
> > >  				LOG(V4L2, Error)
> > >  					<< "Unsupported payload control type "
> > > @@ -213,7 +210,7 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  				return {};
> > >  			}
> > >
> > > -			ControlValue &value = ctrl.second;
> > > +			ControlValue &value = ctrl->second;
> > >  			value.reserve(type, true, info.elems);
> > >  			Span<uint8_t> data = value.data();
> > >
> > > @@ -221,21 +218,20 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  			v4l2Ctrls[i].size = data.size();
> > >  		}
> > >
> > > -		v4l2Ctrls[i].id = id;
> > > -		i++;
> > > +		v4l2Ctrl.id = id;
> >
> > However, shouldn't we try to squeeze the two loops (the one that
> > initialize ctrls and this one) in a single one now that the ordering
> > restrictions is gone ?
>
> That's what I replied to 1/4 :-) I'd have a small preference for doing
> it in 1/4 as that's where the restriction is removed, or at least in a
> patch inserted before 1/4 and 2/4.
>

Wherever works !

Thanks
   j

> > >  	}
> > >
> > >  	struct v4l2_ext_controls v4l2ExtCtrls = {};
> > >  	v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL;
> > > -	v4l2ExtCtrls.controls = v4l2Ctrls;
> > > -	v4l2ExtCtrls.count = count;
> > > +	v4l2ExtCtrls.controls = v4l2Ctrls.data();
> > > +	v4l2ExtCtrls.count = v4l2Ctrls.size();
> > >
> > >  	int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls);
> > >  	if (ret) {
> > >  		unsigned int errorIdx = v4l2ExtCtrls.error_idx;
> > >
> > >  		/* Generic validation error. */
> > > -		if (errorIdx == 0 || errorIdx >= count) {
> > > +		if (errorIdx == 0 || errorIdx >= v4l2Ctrls.size()) {
> > >  			LOG(V4L2, Error) << "Unable to read controls: "
> > >  					 << strerror(-ret);
> > >  			return {};
> > > @@ -244,10 +240,11 @@ ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
> > >  		/* A specific control failed. */
> > >  		LOG(V4L2, Error) << "Unable to read control " << errorIdx
> > >  				 << ": " << strerror(-ret);
> > > -		count = errorIdx - 1;
> > > +
> > > +		v4l2Ctrls.resize(errorIdx);
> > >  	}
> > >
> > > -	updateControls(&ctrls, v4l2Ctrls, count);
> > > +	updateControls(&ctrls, v4l2Ctrls.data(), v4l2Ctrls.size());
> > >
> > >  	return ctrls;
> > >  }
>
> --
> Regards,
>
> Laurent Pinchart

Patch
diff mbox series

diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index 3c32d682..617e6ec9 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -173,8 +173,7 @@  void V4L2Device::close()
  */
 ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
 {
-	unsigned int count = ids.size();
-	if (count == 0)
+	if (ids.empty())
 		return {};
 
 	ControlList ctrls{ controls_ };
@@ -190,22 +189,20 @@  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
 		ctrls.set(id, {});
 	}
 
-	struct v4l2_ext_control v4l2Ctrls[count];
-	memset(v4l2Ctrls, 0, sizeof(v4l2Ctrls));
+	std::vector<v4l2_ext_control> v4l2Ctrls(ctrls.size());
+	memset(v4l2Ctrls.data(), 0, sizeof(v4l2_ext_control) * ctrls.size());
 
-	unsigned int i = 0;
-	for (auto &ctrl : ctrls) {
-		unsigned int id = ctrl.first;
+	for (auto [ctrl, i] = std::pair(ctrls.begin(), 0u); i < ctrls.size(); ctrl++, i++) {
+		const unsigned int id = ctrl->first;
 		const struct v4l2_query_ext_ctrl &info = controlInfo_[id];
+		v4l2_ext_control &v4l2Ctrl = v4l2Ctrls[i];
 
 		if (info.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) {
 			ControlType type;
-
 			switch (info.type) {
 			case V4L2_CTRL_TYPE_U8:
 				type = ControlTypeByte;
 				break;
-
 			default:
 				LOG(V4L2, Error)
 					<< "Unsupported payload control type "
@@ -213,7 +210,7 @@  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
 				return {};
 			}
 
-			ControlValue &value = ctrl.second;
+			ControlValue &value = ctrl->second;
 			value.reserve(type, true, info.elems);
 			Span<uint8_t> data = value.data();
 
@@ -221,21 +218,20 @@  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
 			v4l2Ctrls[i].size = data.size();
 		}
 
-		v4l2Ctrls[i].id = id;
-		i++;
+		v4l2Ctrl.id = id;
 	}
 
 	struct v4l2_ext_controls v4l2ExtCtrls = {};
 	v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL;
-	v4l2ExtCtrls.controls = v4l2Ctrls;
-	v4l2ExtCtrls.count = count;
+	v4l2ExtCtrls.controls = v4l2Ctrls.data();
+	v4l2ExtCtrls.count = v4l2Ctrls.size();
 
 	int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls);
 	if (ret) {
 		unsigned int errorIdx = v4l2ExtCtrls.error_idx;
 
 		/* Generic validation error. */
-		if (errorIdx == 0 || errorIdx >= count) {
+		if (errorIdx == 0 || errorIdx >= v4l2Ctrls.size()) {
 			LOG(V4L2, Error) << "Unable to read controls: "
 					 << strerror(-ret);
 			return {};
@@ -244,10 +240,11 @@  ControlList V4L2Device::getControls(const std::vector<uint32_t> &ids)
 		/* A specific control failed. */
 		LOG(V4L2, Error) << "Unable to read control " << errorIdx
 				 << ": " << strerror(-ret);
-		count = errorIdx - 1;
+
+		v4l2Ctrls.resize(errorIdx);
 	}
 
-	updateControls(&ctrls, v4l2Ctrls, count);
+	updateControls(&ctrls, v4l2Ctrls.data(), v4l2Ctrls.size());
 
 	return ctrls;
 }